Flutter为对话框添加更多选项

时间:2019-07-15 06:14:58

标签: flutter drag-and-drop dialog

有什么办法可以使拖放对话框变得扑朔迷离吗?例如,在屏幕中央显示对话框之后,我想将其拖动到屏幕顶部以在当前封面上显示全屏对话框,例如,此代码是显示对话框的简单实现,我不确定,我该怎么做< / p>

enter image description here

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(title: 'Flutter Demo', theme: ThemeData(), home: Page());
  }
}

class Page extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: RaisedButton.icon(
            onPressed: () {
              showDialog(
                context: context,
                builder: (_) => FunkyOverlay(),
              );
            },
            icon: Icon(Icons.message),
            label: Text("PopUp!")),
      ),
    );
  }
}

class FunkyOverlay extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => FunkyOverlayState();
}

class FunkyOverlayState extends State<FunkyOverlay>
    with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation<double> scaleAnimation;

  @override
  void initState() {
    super.initState();

    controller =
        AnimationController(vsync: this, duration: Duration(milliseconds: 450));
    scaleAnimation =
        CurvedAnimation(parent: controller, curve: Curves.elasticInOut);

    controller.addListener(() {
      setState(() {});
    });

    controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Material(
        color: Colors.transparent,
        child: ScaleTransition(
          scale: scaleAnimation,
          child: Container(
            decoration: ShapeDecoration(
                color: Colors.white,
                shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(15.0))),
            child: Padding(
              padding: const EdgeInsets.all(50.0),
              child: Text("Well hello there!"),
            ),
          ),
        ),
      ),
    );
  }
}

2 个答案:

答案 0 :(得分:4)

输出:

enter image description here

您可以尝试一下。

void main() => runApp(MaterialApp(home: HomePage()));

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool _shown = false;
  double _topOffset = 20, _dialogHeight = 400;
  Duration _duration = Duration(milliseconds: 400);
  Offset _offset, _initialOffset;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    var size = MediaQuery.of(context).size;
    _offset = Offset(size.width, (size.height - _dialogHeight) / 2);
    _initialOffset = _offset;
  }

  @override
  Widget build(BuildContext context) {
    var appBarColor = Colors.blue[800];
    return Scaffold(
      floatingActionButton: FloatingActionButton(onPressed: () => setState(() => _shown = !_shown)),
      body: SizedBox.expand(
        child: Stack(
          children: <Widget>[
            Container(
              color: appBarColor,
              child: SafeArea(
                bottom: false,
                child: Align(
                  child: Column(
                    children: <Widget>[
                      MyAppBar(
                        title: "Image",
                        color: appBarColor,
                        icon: Icons.home,
                        onPressed: () {},
                      ),
                      Expanded(child: Image.asset("assets/images/landscape.jpeg", fit: BoxFit.cover)),
                    ],
                  ),
                ),
              ),
            ),
            AnimatedOpacity(
              opacity: _shown ? 1 : 0,
              duration: _duration,
              child: Material(
                elevation: 8,
                color: Colors.grey[900].withOpacity(0.5),
                child: _shown
                    ? GestureDetector(
                  onTap: () => setState(() => _shown = !_shown),
                  child: Container(color: Colors.transparent, child: SizedBox.expand()),
                )
                    : SizedBox.shrink(),
              ),
            ),

            // this shows our dialog
            Positioned(
              top: _offset.dy,
              left: 10,
              right: 10,
              height: _shown ? null : 0,
              child: AnimatedOpacity(
                duration: _duration,
                opacity: _shown ? 1 : 0,
                child: GestureDetector(
                  onPanUpdate: (details) => setState(() => _offset += details.delta),
                  onPanEnd: (details) {
                    // when tap is lifted and current y position is less than set _offset, navigate to the next page
                    if (_offset.dy < _topOffset) {
                      Navigator.push(
                        context,
                        PageRouteBuilder(
                          pageBuilder: (context, anim1, anim2) => Screen2(),
                          transitionDuration: _duration,
                          transitionsBuilder: (context, anim1, anim2, child) {
                            bool isForward = anim1.status == AnimationStatus.forward;
                            Tween<double> tween = Tween(begin: isForward ? 0.9 : 0.5, end: 1);
                            return ScaleTransition(
                              scale: tween.animate(
                                CurvedAnimation(
                                  parent: anim1,
                                  curve: isForward ? Curves.bounceOut : Curves.easeOut,
                                ),
                              ),
                              child: child,
                            );
                          },
                        ),
                      ).then((_) {
                        _offset = _initialOffset;
                      });
                    }
                    // make the dialog come back to the original position
                    else {
                      Timer.periodic(Duration(milliseconds: 5), (timer) {
                        if (_offset.dy < _initialOffset.dy - _topOffset) {
                          _offset = Offset(_offset.dx, _offset.dy + 15);
                          setState(() {});
                        } else if (_offset.dy > _initialOffset.dy + _topOffset) {
                          _offset = Offset(_offset.dx, _offset.dy - 15);
                          setState(() {});
                        } else
                          timer.cancel();
                      });
                    }
                  },
                  child: Column(
                    children: <Widget>[
                      Icon(Icons.keyboard_arrow_up, color: Colors.white, size: 32),
                      Hero(
                        tag: "MyTag",
                        child: SizedBox(
                          height: _dialogHeight, // makes sure we don't exceed than our specified height
                          child: SingleChildScrollView(child: CommonWidget(appBar: MyAppBar(title: "FlutterLogo", color: Colors.orange))),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// this app bar is used in 1st and 2nd screen
class MyAppBar extends StatelessWidget {
  final String title;
  final Color color;
  final IconData icon;
  final VoidCallback onPressed;

  const MyAppBar({Key key, @required this.title, @required this.color, this.icon, this.onPressed}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: kToolbarHeight,
      color: color,
      width: double.maxFinite,
      alignment: Alignment.centerLeft,
      child: Row(
        children: <Widget>[
          icon != null ? IconButton(icon: Icon(icon), onPressed: onPressed, color: Colors.white,) : SizedBox(width: 16),
          Text(
            title,
            style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
          ),
        ],
      ),
    );
  }
}

// this is the one which is shown in both Dialog and Screen2
class CommonWidget extends StatelessWidget {
  final bool isFullscreen;
  final Widget appBar;

  const CommonWidget({Key key, this.isFullscreen = false, this.appBar}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    var child = Container(
      width: double.maxFinite,
      color: Colors.blue,
      child: FlutterLogo(size: 300, colors: Colors.orange),
    );
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: <Widget>[
        appBar,
        isFullscreen ? Expanded(child: child) : child,
      ],
    );
  }
}

class Screen2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appBarColor = Colors.orange;
    return Scaffold(
      body: Container(
        color: appBarColor,
        child: SafeArea(
          bottom: false,
          child: CommonWidget(
            isFullscreen: true,
            appBar: MyAppBar(
              title: "FlutterLogo",
              color: appBarColor,
              icon: Icons.arrow_back,
              onPressed: () => Navigator.pop(context),
            ),
          ),
        ),
      ),
    );
  }
}

答案 1 :(得分:2)

这是一种实现方式

import 'package:flutter/material.dart';

main() {
  runApp(MaterialApp(
    theme: ThemeData(
      primarySwatch: Colors.indigo,
    ),
    home: App(),
  ));
}

class App extends StatefulWidget {
  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Container(),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.open_in_new),
        onPressed: () {
          showGeneralDialog(
            context: context,
            barrierDismissible: true,
            barrierLabel: "hi",
            barrierColor: Colors.black.withOpacity(0.2),
            transitionDuration: Duration(milliseconds: 500),
            pageBuilder: (context, pAnim, sAnim) {
              return SafeArea(child: FloatingDialog());
            },
            transitionBuilder: (context, pAnim, sAnim, child) {
              if (pAnim.status == AnimationStatus.reverse) {
                return FadeTransition(
                  opacity: Tween(begin: 0.0, end: 0.0).animate(pAnim),
                  child: child,
                );
              } else {
                return FadeTransition(
                  opacity: pAnim,
                  child: child,
                );
              }
            },
          );
        },
      ),
    );
  }
}

class FloatingDialog extends StatefulWidget {
  @override
  _FloatingDialogState createState() => _FloatingDialogState();
}

class _FloatingDialogState extends State<FloatingDialog>
    with TickerProviderStateMixin {
  double _dragStartYPosition;
  double _dialogYOffset;

  Widget myContents = MyScaffold();

  AnimationController _returnBackController;
  Animation<double> _dialogAnimation;

  @override
  void initState() {
    super.initState();
    _dialogYOffset = 0.0;
    _returnBackController =
        AnimationController(vsync: this, duration: Duration(milliseconds: 1300))
          ..addListener(() {
            setState(() {
              _dialogYOffset = _dialogAnimation.value;
              print(_dialogYOffset);
            });
          });
  }

  @override
  void dispose() {
    _returnBackController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(
        top: 100.0,
        bottom: 10.0,
        left: 10.0,
        right: 10.0,
      ),
      child: Transform.translate(
        offset: Offset(0.0, _dialogYOffset),
        child: Column(
          children: <Widget>[
            Icon(
              Icons.keyboard_arrow_up,
              color: Colors.white,
            ),
            Expanded(
              child: GestureDetector(
                onVerticalDragStart: (dragStartDetails) {
                  _dragStartYPosition = dragStartDetails.globalPosition.dy;
                  print(dragStartDetails.globalPosition);
                },
                onVerticalDragUpdate: (dragUpdateDetails) {
                  setState(() {
                    _dialogYOffset = (dragUpdateDetails.globalPosition.dy) -
                        _dragStartYPosition;
                  });
                  print(_dialogYOffset);
                  if (_dialogYOffset < -90.0) {
                    Navigator.of(context).pop();
                    Navigator.of(context).push(
                      PageRouteBuilder(
                          pageBuilder: (context, pAnim, sAnim) => myContents,
                          transitionDuration: Duration(milliseconds: 500),
                          transitionsBuilder: (context, pAnim, sAnim, child) {
                            if (pAnim.status == AnimationStatus.forward) {
                              return ScaleTransition(
                                scale: Tween(begin: 0.8, end: 1.0).animate(
                                    CurvedAnimation(
                                        parent: pAnim,
                                        curve: Curves.elasticOut)),
                                child: child,
                              );
                            } else {
                              return FadeTransition(
                                opacity: pAnim,
                                child: child,
                              );
                            }
                          }),
                    );
                  }
                },
                onVerticalDragEnd: (dragEndDetails) {
                  _dialogAnimation = Tween(begin: _dialogYOffset, end: 0.0)
                      .animate(CurvedAnimation(
                          parent: _returnBackController,
                          curve: Curves.elasticOut));
                  _returnBackController.forward(from: _dialogYOffset);

                  _returnBackController.forward(from: 0.0);
                },
                child: myContents,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class MyScaffold extends StatelessWidget {
  const MyScaffold({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Channels"),
      ),
      body: Center(
        child: RaisedButton(
          onPressed: () {
            Navigator.of(context).pop();
            Navigator.of(context).push(
              MaterialPageRoute(
                builder: (context) => Scaffold(
                  appBar: AppBar(),
                  body: Placeholder(),
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}