Flutter adding more options for dialogs

2019-08-15 17:47发布

is any solution to make drag and drop dialogs in flutter? for example after showing dialog in center of screen i would like to drag it to top of screen to make fullscreen dialog over current cover, for example this code is simple implementation to show dialog and i'm not sure, how can i do that

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条回答
我欲成王,谁敢阻挡
2楼-- · 2019-08-15 18:27

Output:

enter image description here

You can try this.

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),
            ),
          ),
        ),
      ),
    );
  }
}
查看更多
走好不送
3楼-- · 2019-08-15 18:27

This is one way to do it ,

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(),
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}
查看更多
登录 后发表回答