如何在 Flutter 中移动屏幕中的小部件

How to move a widget in the screen in Flutter

我正在使用 flutter,我有一个 容器,使用此代码

new Container(
 width: 50.0,
  height: 50.0,
   decoration: new BoxDecoration(
   shape: BoxShape.circle)

我想让这个圆圈像这样在屏幕上移动

我该怎么做?

您正在寻找的是 Draggable 小部件。然后,您可以使用传递的 onDraggableCanceled 和可用于更新展示位置的偏移量来处理翻译

onDraggableCanceled :(velocity,offset){ 
//update the position here
} 

更新

检查图像后,您需要 "Drop me here" 部分成为一个 DragTarget,它有一个方法 onAccept,当您拖放 Draggable 时,该方法将处理逻辑]

您可以使用 Draggable class 来拖动您想要拖动的项目并将其放置或粘贴到屏幕上的某个位置,您必须用 [=12= 包裹该项目] class。在 DragTarget class onAccept 方法中,您可以在其中编写逻辑。您也可以在这里参考我的代码

import 'package:flutter/material.dart';
void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.indigo,
      ),
      home: new MyHomePage(title: 'Flutter Demo Drag Box'),
    );
  }
}

class MyHomePage extends StatelessWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(title),
      ),
      body:
          new DragGame(), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

class DragGame extends StatefulWidget {
  @override
  _DragGameState createState() => new _DragGameState();
}

class _DragGameState extends State<DragGame> {
  int boxNumberIsDragged;

  @override
  void initState() {
    boxNumberIsDragged = null;
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return new Container(
        constraints: BoxConstraints.expand(),
        color: Colors.grey,
        child: new Stack(
          children: <Widget>[
            buildDraggableBox(1, Colors.red, new Offset(30.0, 100.0)),
            buildDraggableBox(2, Colors.yellow, new Offset(30.0, 200.0)),
            buildDraggableBox(3, Colors.green, new Offset(30.0, 300.0)),
          ],
        ));
  }

  Widget buildDraggableBox(int boxNumber, Color color, Offset offset) {
    return new Draggable(
      maxSimultaneousDrags: boxNumberIsDragged == null || boxNumber == boxNumberIsDragged ? 1 : 0,
      child: _buildBox(color, offset),
      feedback: _buildBox(color, offset),
      childWhenDragging: _buildBox(color, offset, onlyBorder: true),
      onDragStarted: () {
        setState((){
          boxNumberIsDragged = boxNumber;
        });
      },
      onDragCompleted: () {
        setState((){
          boxNumberIsDragged = null;
        });
      },
      onDraggableCanceled: (_,__) {
        setState((){
          boxNumberIsDragged = null;
        });
      },
    );
  }

  Widget _buildBox(Color color, Offset offset, {bool onlyBorder: false}) {
    return new Container(
      height: 50.0,
      width: 50.0,
      margin: EdgeInsets.only(left: offset.dx, top: offset.dy),
      decoration: BoxDecoration(
          color: !onlyBorder ? color : Colors.grey,
          border: Border.all(color: color)),
    );
  }
}

这里是:

import 'package:flutter/material.dart';

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Drag app"),
        ),
        body: HomePage(),
      ),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _HomePageState();
  }
}

class _HomePageState extends State<HomePage> {
  double width = 100.0, height = 100.0;
  Offset position ;

  @override
  void initState() {
    super.initState();
    position = Offset(0.0, height - 20);
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        Positioned(
          left: position.dx,
          //top: position.dy - height + 20,
          child: Draggable(
            child: Container(
              width: width,
              height: height,
              color: Colors.blue,
              child: Center(child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
            ),
            feedback: Container(
              child: Center(
                child: Text("Drag", style: Theme.of(context).textTheme.headline,),),
              color: Colors.red[800],
              width: width,
              height: height,
            ),
            onDraggableCanceled: (Velocity velocity, Offset offset){
              setState(() => position = offset);
            },
          ),
        ),
      ],
    );
  }
}

首先,用 PositionedContainer 包裹在 Stack 中。

然后,使用Pan Gesture在你的Container中实现一个Pan并使用onPan...方法来处理Pan Gesture

代码如下:

偏移位置;

@override
  void initState() {
    super.initState();
    position = Offset(10, 10);
  }    

@override
    Widget build(BuildContext context) {

        double _width = MediaQuery.of(context).size.width;
        double _height = _width * 9 / 16;


        return GestureDetector(
          onPanStart: (details) => _onPanStart(context, details),
          onPanUpdate: (details) => _onPanUpdate(context, details, position),
          onPanEnd: (details) => _onPanEnd(context, details),
          onPanCancel: () => _onPanCancel(context),

          child: SafeArea(
            child: Stack(
              children: <Widget>[
                Positioned(
                  top: position.dy,
                  child: Container(
                    color: Colors.red,
                    width: _width,
                    height: _height,
                  ),
                ),
              ],
            ),
          ),
        );
      }

      void _onPanStart(BuildContext context, DragStartDetails details) {
        print(details.globalPosition.dy);

      }

      void _onPanUpdate(BuildContext context, DragUpdateDetails details, Offset offset) {
        setState(() {
          position = details.globalPosition;
        });
      }

      void _onPanEnd(BuildContext context, DragEndDetails details) {
        print(details.velocity);
      }

      void _onPanCancel(BuildContext context) {
        print("Pan canceled !!");
      }

希望对您有所帮助!

下面是整个过程

首先我们构建我们的骨架应用程序。然后我们可以将多个盒子嵌入到这个骨架中,每个盒子都有一个 Offset、一个 Color 和一个 Label stringOffset 确定框在给定时刻的位置,它具有初始状态和根据用户拖动框的位置更新的状态。

然后创建一个使用 DragTarget Classstatic UI element。我们可以将 Draggable Boxes 拖到 DragTarget widget 上,将其颜色更改为 Draggable Box.

的颜色

完整示例:

class AppState extends State<App> {
  Color caughtColor = Colors.deepPurple;

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Stack(
        children: <Widget>[
           DragBox(Offset(0.0, 0.0), 'Box One', Colors.blueAccent),
           DragBox(Offset(150.0, 0.0), 'Box Two', Colors.orange),
           DragBox(Offset(300.0, 0.0), 'Box Three', Colors.lightGreen),
          Positioned(
            left: 125.0,
            bottom: 0.0,
            child: DragTarget(
              onAccept: (Color color) {
                caughtColor = color;
              },
              builder: (
                  BuildContext context,
                  List<dynamic> accepted,
                  List<dynamic> rejected,
                  ) {
                return Container(
                  width: 150.0,
                  height: 150.0,
                  decoration: BoxDecoration(
                    color: accepted.isEmpty ? caughtColor : Colors.deepPurple.shade200,
                  ),
                  child: Center(
                    child: Text("Drag Here!", style: TextStyle(color: Colors.white)),
                  ),
                );
              },
            ),
          )
        ],
      ),
    );
  }
}

class DragBox extends StatefulWidget {
  final Offset initPos;
  final String label;
  final Color itemColor;

  DragBox(this.initPos, this.label, this.itemColor);

  @override
  DragBoxState createState() => DragBoxState();
}

class DragBoxState extends State<DragBox> {
  Offset position = Offset(0.0, 0.0);

  @override
  void initState() {
    super.initState();
    position = widget.initPos;
  }

  @override
  Widget build(BuildContext context) {
    return Positioned(
        left: position.dx,
        top: position.dy,
        child: Draggable(
          data: widget.itemColor,
          child: Container(
            width: 100.0,
            height: 100.0,
            color: widget.itemColor,
            child: Center(
              child: Text(
                widget.label,
                style: TextStyle(
                  color: Colors.white,
                  decoration: TextDecoration.none,
                  fontSize: 20.0,
                ),
              ),
            ),
          ),
          onDraggableCanceled: (velocity, offset) {
            setState(() {
              position = offset;
            });
          },
          feedback: Container(
            width: 120.0,
            height: 120.0,
            color: widget.itemColor.withOpacity(0.5),
            child: Center(
              child: Text(
                widget.label,
                style: TextStyle(
                  color: Colors.white,
                  decoration: TextDecoration.none,
                  fontSize: 18.0,
                ),
              ),
            ),
          ),
        ));
  }
}

参考:Building a Drag and Drop Application