文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

flutter如何实现点击下拉栏微信右上角弹出窗功能

2023-06-15 04:08

关注

小编给大家分享一下flutter如何实现点击下拉栏微信右上角弹出窗功能,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

先看效果实现

flutter如何实现点击下拉栏微信右上角弹出窗功能

需求分析

这个是使用 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;}

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

页面分两部分

flutter如何实现点击下拉栏微信右上角弹出窗功能

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

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

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 happensclass _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(),              ),            ),          ),        ],      ),    );  }}

然后就能实现我们的弹窗动画了,如果想要其他效果的动画,可以手动替换动画类,或者自己手写个新的最后我自己的项目修饰效果。

以上是“flutter如何实现点击下拉栏微信右上角弹出窗功能”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网行业资讯频道!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯