先看效果实现

需求分析

这个是使用 PopupRoute这个路由类进行实现

大概原理就是利用PopupRpute这个类进行改造,然后自定义一个页面,页面内镶嵌一个动画类,用来实现缩放动画

大概分为三部分,PopupRoute改造,弹出页面设置,动画类设置。

为什么选择PopupRoute?

可以镶嵌在flutter本身的路由管理之中

也就是逻辑操作都是正常的页面管理,可以手动管理,也可以用路由返回直接关掉,不会影响原有页面和布局

第一步,改造PopupRoute类

import 'package:flutter/material.dart';

class Popup extends PopupRoute {
  final Duration _duration = Duration(milliseconds: 300);
  Widget child;

  Popup({@required this.child});

  @override
  Color get barrierColor => null;

  @override
  bool get barrierDismissible => true;

  @override
  String get barrierLabel => null;

  @override
  Widget buildPage(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation) {
    return child;
  }

  @override
  Duration get transitionDuration => _duration;
}

第二步,新建一个弹窗页面

页面分两部分

一个是页面的背景,一个是页面的内容

注意,弹窗动画的代码在下方

class Model extends StatefulWidget {
  final double left; //距离左边位置 弹窗的x轴定位
  final double top; //距离上面位置 弹窗的y轴定位
  final bool otherClose; //点击背景关闭页面
  final Widget child; //传入弹窗的样式
  final Function fun; // 把关闭的函数返回给父组件 参考vue的$emit
  final Offset offset; // 弹窗动画的起点

  Model({
    @required this.child,
    this.left = 0,
    this.top = 0,
    this.otherClose = false,
    this.fun,
    this.offset,
  });


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


class _ModelState extends State<Model> {
  AnimationController animateController;


  @override
  Widget build(BuildContext context) {
    return Material(
      color: Colors.transparent,
      child: Stack(
        children: <Widget>[
          Positioned(
            child: GestureDetector(
              child: Container(
                width: MediaQuery.of(context).size.width,
                height: MediaQuery.of(context).size.height,
                color: Colors.transparent,
              ),
              onTap: () async {
                if (widget.otherClose) {
                } else {
                  closeModel();
                }
              },
            ),
          ),
          Positioned(
            /// 这个是弹窗动画 在下方,我把他分离 防止太长
            child: ZoomInOffset(
              duration: Duration(milliseconds: 180),
              offset: widget.offset,
              controller: (controller) {
                animateController = controller;
                widget.fun(closeModel);
              },
              child: widget.child,
            ),
            left: widget.left,
            top: widget.top,
          ),
        ],
      ),
    );
  }


  ///关闭页面动画
  Future closeModel() async {
    await animateController.reverse();
    Navigator.pop(context);
  }
}

动画代码

我是直接复制animate_do:^2.0.0 这个版本的ZoomIn的动画类

这个插件本身就是依赖flutter 自带的动画来完成的,很简洁,使用很方便,不过默认构造的时候没有动画的启动方向,默认是最中心。但是可以添加个参数,我把源码复制出来自己改造了一下。这个类在构造的时候有个controller 参数,类型的函数,带一个AnimationController的参数把控制器通过函数传递出去到Model类,可以在Model类里面进行控制动画开启和关闭后续我在Model类里面把动画关闭和返回退出PopupRoute层封装成一个函数 传递到Model里面的fun参数里面返回出去可以在最外部进行组件通信,进而控制这些子组件

import 'package:flutter/material.dart';

class ZoomInOffset extends StatefulWidget {
  final Key key;
  final Widget child;
  final Duration duration;
  final Duration delay;


  ///把控制器通过函数传递出去,可以在父组件进行控制
  final Function(AnimationController) controller;
  final bool manualTrigger;
  final bool animate;
  final double from;


  ///这是我自己写的 起点
  final Offset offset;


  ZoomInOffset(
      {this.key,
      this.child,
      this.duration = const Duration(milliseconds: 500),
      this.delay = const Duration(milliseconds: 0),
      this.controller,
      this.manualTrigger = false,
      this.animate = true,
      this.offset,
      this.from = 1.0})
      : super(key: key) {
    if (manualTrigger == true && controller == null) {
      throw FlutterError('If you want to use manualTrigger:true, \n\n'
          'Then you must provide the controller property, that is a callback like:\n\n'
          ' ( controller: AnimationController) => yourController = controller \n\n');
    }
  }


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


/// State class, where the magic happens
class _ZoomInState extends State<ZoomInOffset>
    with SingleTickerProviderStateMixin {
  AnimationController controller;
  bool disposed = false;
  Animation<double> fade;
  Animation<double> opacity;


  @override
  void dispose() async {
    disposed = true;
    controller.dispose();
    super.dispose();
  }


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


    controller = AnimationController(duration: widget.duration, vsync: this);
    fade = Tween(begin: 0.0, end: widget.from)
        .animate(CurvedAnimation(curve: Curves.easeOut, parent: controller));


    opacity = Tween<double>(begin: 0.0, end: 1)
        .animate(CurvedAnimation(parent: controller, curve: Interval(0, 0.65)));


    if (!widget.manualTrigger && widget.animate) {
      Future.delayed(widget.delay, () {
        if (!disposed) {
          controller?.forward();
        }
      });
    }


    if (widget.controller is Function) {
      widget.controller(controller);
    }
  }


  @override
  Widget build(BuildContext context) {
    if (widget.animate && widget.delay.inMilliseconds == 0) {
      controller?.forward();
    }


    return AnimatedBuilder(
      animation: fade,
      builder: (BuildContext context, Widget child) {
        ///  这个transform有origin的可选构造参数,我们可以手动添加
        return Transform.scale(
          origin: widget.offset,
          scale: fade.value,
          child: Opacity(
            opacity: opacity.value,
            child: widget.child,
          ),
        );
      },
    );
  }
}

最后页面调用

我用stack类进行堆叠组件,堆叠出上面箭头

其实可以抽成一个方向设置不过太麻烦了我没写,毕竟能用就行

import 'package:flutter/material.dart';
import 'package:one/widget/Model.dart';
import 'package:one/widget/Popup.dart';

void main() {
  runApp(MyApp());
}


class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}


class _MyAppState extends State<MyApp> {
  ///给获取详细信息的widget设置一个key
  GlobalKey iconkey = GlobalKey();


  ///获取位置,给后续弹窗设置位置
  Offset iconOffset;


  ///获取size 后续计算弹出位置
  Size iconSize;


  ///接受弹窗类构造成功传递来的关闭参数
  Function closeModel;


  @override
  Widget build(BuildContext context) {
    ///等待widget初始化完成
    WidgetsBinding.instance.addPostFrameCallback((duration) {
      ///通过key获取到widget的位置
      RenderBox box = iconkey.currentContext.findRenderObject();


      ///获取widget的高宽
      iconSize = box.size;


      ///获取位置
      iconOffset = box.localToGlobal(Offset.zero);
    });


    return MaterialApp(
      home: Builder(
        builder: (context) => Scaffold(
          appBar: AppBar(
            actions: [
              IconButton(
                key: iconkey,
                icon: Icon(
                  Icons.favorite,
                  color: Colors.red,
                ),
                onPressed: () {
                  showModel(context);
                },
              ),
            ],
          ),
          body: Column(
            children: [],
          ),
        ),
      ),
    );
  }


  ///播放动画
  void showModel(BuildContext context) {
    /// 设置传入弹窗的高宽
    double _width = 130;
    double _height = 230;


    Navigator.push(
      context,
      Popup(
        child: Model(
          left: iconOffset.dx - _width   iconSize.width / 1.2,
          top: iconOffset.dy   iconSize.height / 1.3,
          offset: Offset(_width / 2, -_height / 2),
          child: Container(
            width: _width,
            height: _height,
            child: buildMenu(),
          ),
          fun: (close) {
            closeModel = close;
          },
        ),
      ),
    );
  }


  ///构造传入的widget
  Widget buildMenu() {
    ///构造List
    List _list = [1, 2, 3, 4, 5];


    return Container(
      height: 160,
      width: 230,
      child: Stack(
        children: [
          Positioned(
            right: 4,
            top: 17,
            child: Container(
              width: 20,
              height: 20,
              transform: Matrix4.rotationZ(45 * 3.14 / 180),
              decoration: BoxDecoration(
                color: Color.fromRGBO(46, 53, 61, 1),
                borderRadius: BorderRadius.circular(5),
              ),
            ),
          ),


          ///菜单内容
          Positioned(
            bottom: 0,
            child: Container(
              padding: EdgeInsets.only(
                top: 20,
                bottom: 20,
                left: 10,
                right: 10,
              ),
              width: 130,
              height: 200,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(10),
                color: Color.fromRGBO(46, 53, 61, 1),
              ),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: _list
                    .map<Widget>((e) => InkWell(
                          child: Container(
                            width: double.infinity,
                            alignment: Alignment.center,
                            child: Text(
                              '这应该是选项${e.toString()}',
                              style: TextStyle(
                                color: Colors.white70,
                                fontSize: 14,
                              ),
                            ),
                          ),
                          onTap: () async {
                            print('这是点击了选项${e.toString()}');
                            await Future.delayed(Duration(milliseconds: 500))
                                .then((value) => print('开始'));
                            await closeModel();
                            print('结束');
                          },
                        ))
                    .toList(),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

然后就能实现我们的弹窗动画了,如果想要其他效果的动画,可以手动替换动画类,或者自己手写个新的最后我自己的项目修饰效果,还有demo的代码代码 仓库地址:https://github.com/mannaoz/one

到此这篇关于flutter 实现点击下拉栏微信右上角弹出窗功能的文章就介绍到这了,更多相关flutter弹出窗内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

flutter 实现点击下拉栏微信右上角弹出窗功能的更多相关文章

  1. Swift - 自定义UIActivity分享

    UIActivity可以十分方便地将文字、图片等内容进行分享,比如分享到微信、微博、发送邮件、短信等等。下面通过继承UIActivity实现定制按钮和功能,通过调用UIActivityViewController来展示分享框。,NSURL]//新建自定义的分享对象数组acts=[WeiXinActivity(),147)!important;font-family:Consolas,UIActivityTypePrintUIActivityTypeAssignToContactUIActivityType

  2. Flutter中文教程-Cookbook

    Flutter中文网的Cookbook中包含了在编写Flutter应用程序时常见问题及示例。设计基础使用主题共享颜色和字体样式Images显示来自网上的图片用占位符淡入图片使用缓存图Lists创建一个基本list创建一个水平list使用长列表创建不同类型子项的List创建一个gridList处理手势处理点击添加Material触摸水波效果实现滑动关闭导航导航到新页面并返回给新页面传值从新页面返回数据给上一个页面网络从网上获取数据进行认证请求使用WebSockets

  3. android-studio – 未配置Dart SDK

    Initializinggradle…

  4. 安卓 – 从一个扑动的应用程序拨打电话

    或者有更好的选择从我的应用程序拨打电话?

  5. android – 如何在Flutter中添加Webview?

    我知道可以将WebView添加为整页,但找不到任何示例代码.我假设你可以使用PageView作为它的基础,但不知道如何调用本机androidWebView并将其添加到PageView.谁能指出我正确的方向?

  6. android – 如何将消息从Flutter传递给Native?

    如果需要与特定的API/硬件组件进行交互,您如何将Flutter的信息传递回Android/Native代码?是否有任何事件频道可以通过其他方式发送信息或类似于回调?

  7. android – 如何在Flutter App中处理onPause / onResume?

    我是否过于复杂的事情?即使我的用例似乎不需要它,我仍然想知道:如何自己处理onPause/onResume事件?

  8. android – 如何使用Flutter构建Augment Reality应用程序?

    我对Android开发有一些基础知识.最近听说过Flutter并且非常有兴趣研究它.我想知道是否有可能使用颤振构建增强现实应用程序以及要实现此目的的方法?请帮忙.解决方法截至目前,颤振不支持3D.Flutter现在专注于2D,团队长期计划为颤振提供优化的3Dapi.你读了常见问题here.

  9. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  10. php微信公众平台交互与接口详解

    这篇文章主要为大家详细介绍了php微信公众平台开发,交互与接口,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部