Ad

Flutter Adding More Options For Dialogs

- 1 answer

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!"),
            ),
          ),
        ),
      ),
    );
  }
}
Ad

Answer

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),
            ),
          ),
        ),
      ),
    );
  }
}
Ad
source: stackoverflow.com
Ad