精华内容
下载资源
问答
  • 一、Flutter 手势 - 跟随手指运动的小球、 三、完整代码示例、 三、相关资源





    一、Flutter 手势 - 跟随手指运动的小球



    设置小球坐标变量 : 其中 currentX 是距离左侧边界的距离 , currentY 是距离右侧边界的距离 ;

    
      /// 当前小球的 x 坐标
      double currentX = 0;
      /// 当前小球的 y 坐标
      double currentY = 0;
    

    小球的位置 : 小球是在 Stack 帧布局中的 Positioned 组件 , 其 left 和 top 字段值设置其坐标 , 分别对应 currentX 和 currentY 值 ;

    // 小球
    Positioned(
      /// 当前位置
      left: currentX,
      top: currentY,
    )
    

    监听事件 : 监听 GestureDetector 组件的 onPanUpdate 事件 , 其回调方法是 void Function(DragUpdateDetails details) 类型的 方法 , 可以从 DragUpdateDetails 类型参数中获取当前 x , y 的移动距离 , 该距离需要与之前的距离累加 , 才能得到准确的坐标值 ;

    在回调方法中调用 setState 方法 , 修改成员变量 currentX 和 currentY , 从而修改 Positioned 组件的位置 , 以达到小球移动的目的 ;

    /// 手势检测组件
    child: GestureDetector(
      /// 移动操作
      onPanUpdate: (e){
        setState(() {
          // e 中只能获取到 delta 值 , 需要逐步累加
          currentX += e.delta.dx;
          currentY += e.delta.dy;
        });
      },
    )
    

    代码示例 :

    // 小球
    Positioned(
      /// 当前位置
      left: currentX,
      top: currentY,
      /// 手势检测组件
      child: GestureDetector(
        /// 移动操作
        onPanUpdate: (e){
          setState(() {
            // e 中只能获取到 delta 值 , 需要逐步累加
            currentX += e.delta.dx;
            currentY += e.delta.dy;
          });
        },
        // 黑色小球
        child: Container(
          width: 40,
          height: 40,
          decoration: BoxDecoration(
            color: Colors.black,
            borderRadius: BorderRadius.circular(20),
          ),
        ),
      ),),
    




    三、完整代码示例



    完整代码示例 :

    
    import 'package:flutter/material.dart';
    
    class GesturePage extends StatefulWidget {
      @override
      _GesturePageState createState() => _GesturePageState();
    }
    
    class _GesturePageState extends State<GesturePage> {
    
      /// 当前小球的 x 坐标
      double currentX = 0;
      /// 当前小球的 y 坐标
      double currentY = 0;
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
    
          // 设置主题
          theme: ThemeData(
            primarySwatch: Colors.amber,
          ),
    
          // 设置主体组件
          home: Scaffold(
    
            // 设置标题栏
            appBar: AppBar(
              title: Text("手势检测"),
    
              // 返回按钮设置
              leading: GestureDetector(
                // 点击事件回调函数
                onTap: (){
                  // 退出当前界面
                  Navigator.pop(context);
                },
    
                // 回退按钮图标
                child: Icon(Icons.arrow_back),
              ),
            ),
    
            // 水平/垂直方向平铺组件
            body: FractionallySizedBox(
              // 水平方向平铺
              widthFactor: 1,
    
              // 帧布局
              child: Stack(
                children: <Widget>[
                  // 垂直方向线性布局
                  Column(
                    children: <Widget>[
    
                      // 手势检测组件
                      GestureDetector(
                        // 点击事件
                        onTap: (){
                          print("双击");
                        },
    
                        // 双击事件
                        onDoubleTap: (){
                          print("双击");
                        },
    
                        // 长按事件 , ()=>方法名(参数列表) 即可回调一个现有方法
                        onLongPress: () => _longPress(),
    
                        // 点击取消
                        onTapCancel: (){
                          print("点击取消");
                        },
    
                        // 点击按下
                        onTapDown: (e){
                          print("点击按下");
                        },
    
                        // 点击抬起
                        onTapUp: (e){
                          print("点击抬起");
                        },
    
                        // 手势检测的作用组件 , 监听该组件上的各种手势
                        child: Container(
                          // 子组件居中
                          alignment: Alignment.center,
    
                          // 内边距
                          padding: EdgeInsets.all(100),
    
                          // 背景装饰
                          decoration: BoxDecoration(
                            color: Colors.green,
                          ),
    
                          child: Text(
                            "手势检测",
                            style: TextStyle(
                              fontSize: 50,
                              color: Colors.red,
                            ),
                          ),
    
                        ),
                      )
                    ],
                  ),
    
                  // 小球
                  Positioned(
                    /// 当前位置
                    left: currentX,
                    top: currentY,
    
                    /// 手势检测组件
                    child: GestureDetector(
    
                      /// 移动操作
                      onPanUpdate: (e){
                        setState(() {
                          // e 中只能获取到 delta 值 , 需要逐步累加
                          currentX += e.delta.dx;
                          currentY += e.delta.dy;
                        });
                      },
    
                      // 黑色小球
                      child: Container(
                        width: 40,
                        height: 40,
    
                        decoration: BoxDecoration(
                          color: Colors.black,
                          borderRadius: BorderRadius.circular(20),
                        ),
                      ),
                    ),),
                ],
              ),
            ),
          ),
        );
      }
    
      /// 长按事件
      void _longPress(){
        print("长按");
      }
    }
    
    

    运行效果 :

    在这里插入图片描述





    三、相关资源



    参考资料 :


    博客源码下载 :

    展开全文
  • Arena: Flutter定义了一个Arena手势竞技场,对有冲突的手势进行判断最后选出唯一的一个获胜者并处理事件。例如水平和垂直的ListView会根据横竖的滑动分量进行判断。 Tip: 手势冲突只是手势级别的,而手势是对原始...

    Pointer

    Pointer Event指针事件是指最原始的触摸事件,一次事件包括触摸到屏幕、在屏幕上移动到离开屏幕。
    使用Listener组件来监听指针事件,基本的回调有onPointerDown、onPointerMove、onPointerUp。

    Listener(
      child: Container(
        alignment: Alignment.center,
        color: Colors.blue,
        width: 100,
        height: 100,
      ),
      onPointerDown: (event) => print('onPointerDown'),
      onPointerMove: (event) => print('onPointerMove'),
      onPointerUp: (event) => print('onPointerUp'),
    ),
    

    Listener有一个behavior参数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L5VKk3un-1600399147849)(/images/behavior.jpg)]

    首先要了解两个概念 一个是渲染层级,还有一个是Widget的树结构。当手指点击之后会先通过渲染层级和behavior参数确定HitTest命中的组件,然后根据树结构依次向上传递Pointer Event。透明Widget一般HitTest都会失败,但是behavior参数可以依照如下说明进行设定。

    deferToChild:当子节点widget的HitTest命中测试成功时,该节点一定会响应。

    opaque:将当前节点Widget当作是不透明的进行处理(就算是透明的组件)。

    translucent:透传,正常情况下HitTest只会响应渲染层级上面的组件。

    IgnorePointer组件 被IgnorePointer组件包裹的子树及其本身都不会处理PointerEvent事件。

    AbsorbPointer组件 被AbsorbPointer组件包裹的子树不会处理PointerEvent事件,但是AbsorbPointer组件本身会处理。

    GestureDetector组件

    GestureDetector组件对基本的PointerEvent事件进行了语义封装,通过GestureRecognizer使用Listener将PointerEvent转义成onTap、onDoubleTap、onLongPress、onPanDown、onVeticalDragUpdate等等接口。

    Arena: Flutter定义了一个Arena手势竞技场,对有冲突的手势进行判断最后选出唯一的一个获胜者并处理事件。例如水平和垂直的ListView会根据横竖的滑动分量进行判断。
    Tip: 手势冲突只是手势级别的,而手势是对原始指针的语义化的识别,所以在遇到复杂的冲突场景时,都可以通过Listener直接识别原始指针事件来解决冲突。

    Notification

    NotificationListener组件可以用来监听从子结构传递过来的通知,和手势通知不同,这种Notification可以选择是否还要往上传递。
    常用的是ScrollStartNotification、ScrollUpdateNotification等滑动通知,也可以自定义通知dispatch向上分发。

    More info: CSDN

    展开全文
  • Flutter 手势

    千次阅读 2019-07-09 17:07:13
    Flutter中的手势分为两层,第一层是触摸原始指针(Pointer)事件,描述了屏幕上指针(如触摸、鼠标和触控笔)的位置和移动。 指针(Pointer)代表用户与屏幕交互的原始数据,有四种事件类型: PointerDownEvent: 指针...

    1 概述

    Flutter中的手势分为两层,第一层是触摸原始指针(Pointer)事件,描述了屏幕上指针(如触摸、鼠标和触控笔)的位置和移动。
    指针(Pointer)代表用户与屏幕交互的原始数据,有四种事件类型:

    PointerDownEvent: 指针接触到屏幕
    PointerMoveEvent: 指针从屏幕上的一个位置移动到另一个位置
    PointerUpEvent: 指针停止接触屏幕
    PointerCancelEvent: 指针的输入事件不再针对此应用(事件取消)

    第二层就是我们可以检测到的手势,主要分为三大类:轻击、拖动和缩放。

    2 GestureDetector

    GestureDetector可以进行手势检测,如单击,双击,长按,垂直、水平拖动等。

    GestureDetector事件描述

    事件名描述
    onTapDown点击屏幕立即触发
    onTapUp手指离开屏幕
    onTap点击屏幕
    onTapCancel点击事件结束,onTapDown不会再触发点击事件
    onDoubleTap快速连续两次在同一位置点击屏幕
    onLongPress长按
    onVerticalDragStart与屏幕接触,可能会开始垂直移动
    onVerticalDragUpdate与屏幕接触,已经沿垂直移动
    onVerticalDragEnd先前与屏幕接触并垂直移动的指针不再与屏幕接触,并且在停止接触屏幕时以特定速度移动
    onHorizontalDragStart与屏幕接触,可能会开始水平移动
    onHorizontalDragUpdate与屏幕接触,已经沿水平移动
    onHorizontalDragEnd先前与屏幕接触并水平移动的指针不再与屏幕接触,并在停止接触屏幕时以特定速度移动

    3 Dissmissible

    可用于实现滑动删除。

    常见属性

    属性名类型说明
    onDismissedDismissDirectionCallback当包裹的组件消失后回调
    movementDurationDuration定义组件消失的时长
    onResizeVoidCallback组件大小改变时的回调
    resizedDurationDuration组件大小改变时长
    childWidget子元素,滑动时显示的组件

    4 示例1-实现点击效果

    import 'package:flutter/material.dart';
    
    void main() => runApp(MaterialApp(
          home: HomePage(),
        ));
    
    class HomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: MyButton(),
          ),
        );
      }
    }
    
    class MyButton extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return GestureDetector(
          onTap: () {
            print('onTap');
          },
          child: Container(
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(15.0),
              color: Theme.of(context).buttonColor,
            ),
            padding: EdgeInsets.all(20.0),
            child: Text('MyButton'),
          ),
        );
      }
    }
    
    

    5 示例2-实现滑动删除

    import 'package:flutter/material.dart';
    
    void main() => runApp(MaterialApp(
          home: HomePage(),
        ));
    
    class HomePage extends StatelessWidget {
      final List<String> items = List.generate(20, (index) => 'item $index');
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: ListView.builder(
            itemCount: items.length,
            itemBuilder: (context, index) {
              final item = items[index];
              return Dismissible(
                  onDismissed: (_) {
                    items.removeAt(index);
                  },
                  movementDuration: Duration(milliseconds: 100),
                  key: Key(item),
                  child: ListTile(
                    title: Text('$item'),
                  ),
                  background: Container(color: Color(0xffff0000),),
              );
            },
          ),
        );
      }
    }
    
    
    展开全文
  • flutter手势(事件监听) 和 事件总线

    千次阅读 2020-04-07 13:25:29
    文档:Day3_25 手势(事件监听), 事件总线 和 … 链接:http://note.youdao.com/noteshare?id=67936e30b5c44258a7394387afb36209&sub=878F6BFA1DD046E9909C088B7CBFA512 手势(事件监听) 和 事件总线 路由和导航 ...

    这个转自我自己的有道云 想看图片去那里
    文档:Day3_25 手势(事件监听), 事件总线 和 …
    链接:http://note.youdao.com/noteshare?id=67936e30b5c44258a7394387afb36209&sub=878F6BFA1DD046E9909C088B7CBFA512

    手势(事件监听) 和 事件总线

    路由和导航

    首先我们先解决一个问题

    问题解决

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VsNC7jRC-1586237096386)(DAF4C87A98FD407F928DFDAB55045CB8)]

    如果使用较小的屏幕会出现超出安全区的情况

    我们来分析 不要怕

    这里是因为这个这个Start组件 他是一个Row

    但是这个里面的宽度 大于他本身的宽度

    1. 所以我们一个是可以改小他的size

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5vWp2fB0-1586237096388)(1491748893F64B538432D3CD808BD9E4)]

    这个地方我们希望它能自己变小 所以我们这里可以使用这个Expanded

    这个Expanded的作用是 占据剩下的空间

    我们在外面包裹一层Expanded

    也不行因为他是里面也是一个一个的小组件

    1. 使用FittedBox
      Widget buildContentInfoRate() {
        return FittedBox(
          child: Row(
            children: <Widget>[
              HYStarRating(rating: movie.rating, size: 20),
              SizedBox(width: 6),
              Text("${movie.rating}", style: TextStyle(fontSize: 16))
            ],
          ),
        );
      }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9aWWkYI9-1586237096389)(27F895B7DCE24249874B3B7B9EFE9E1C)]

    它就可以 自动的适配

    如果里面的组件小于外面的组件

    我们就会缩小 里面的组件然后 放下来

    这样没有报错了

    但是这个也不是最好的解决方案

    我们希望 能够自动的适配设备

    我们可以自己定义rpx这个单位

    也可以使用 第三方官方库

    这个专门开课来做

    一. 事件监听

    事件就是点击长按 滑动

    这些都会在手机当中形成事件

    我们这里就是要让程序对这些事件有响应

    flutter中手势分两种

    • 第一原始指针事件
      • 这个东西就是最基础的点击 指针的位置和移动
    • 第二手势识别
      • 这个东西就是对原始事件的封装
      • 只不过这个东西是框架帮忙封装的

    2.1 原始指针事件

    但是官方其实更建议我们使用手势

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wrrjayOP-1586237096390)(1018BB5923F549049C995467C211548C)]

    再开中我们更建议使用手势而不是原始指针事件

    Pointer事件 代表的是人机交互的原始数据 一共有四种事件

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dnEsST08-1586237096390)(88E9A24A7A08410C872655DD0ABA234D)]

    Pointer的原理是什么呢

    • 指针的落下的时候, 框架做了一个hit test的操作
    • 事件会沿着最内部的组件向根冒泡分发
    • 并不存在用于取消或者停止指针事件进一步分发的机制

    而且注意我们是没有办法直接停掉这个 冒泡的过程的

    那我们到底怎么来做这个

    我们到底怎么来做这个监听的呢

    我们还是建立最基础的东西

    根据它事件的名称 很明显 这个地方

    • up 监听它抬起
    • down 监听它按下
    • move 监听它移动
    • Cancel 它取消的动作有些时候这个手势可能会被一些东西打断这个时候我们就可以用这个东西

    比如说我们手指按下的同时我们的电话来了 这个时候我们就触发这个事件

    或者说我们的闹钟响了

    这个时候 我们可以嵌套一个Listener然后来监听它的 事件

    它这里有一个onPointerDown: 这个就是点击触发的回调函数

    它有一个参数

    class HYHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Listener(
              onPointerDown: (event) {
                print(event.position);
              },
              child: Container(
                color: Colors.red,
                width: 200.px,
                height: 200.px
              ),
            ),
          ),
        );
      }
    }
    

    同样我们也可以触发它的其他的方法

    import "package:flutter/material.dart";
    import 'package:learn_flutter02/extension/size_fit.dart';
    
    import "../extension/int_extension.dart";
    
    main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        HYSizeFit.initialize();
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          home: HYHomePage(),
        );
      }
    }
    
    class HYHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Listener(
              onPointerDown: (event) {
                print("手指按下 ${event}");
              },
              onPointerMove: (event) {
                print("手指移动 $event");
              },
              onPointerSignal: (event) {
                print("sinal $event");
              },
              onPointerCancel: (event) {
                print("Cancel $event");
              },
              onPointerUp: (event) {
                print("手指抬起: $event");
              },
              child: Container(
                color: Colors.red,
                width: 200.px,
                height: 200.px
              ),
            ),
          ),
        );
      }
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ob6OHY8g-1586237096391)(0DE82B3AAF504424AC76C91A05E69FCA)]

    但是实际上这个event他经常用的是position 位置之类的信息

    这里我们可以拿到他的position

              onPointerDown: (event) {
                print("手指按下 ${event.position}");
              },
              
              
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AK6dImUE-1586237096391)(4D740E2352374A378A713F58FFE75795)]

    但是这个是相对于屏幕的位置

    我们也可以获得相对他的子Widget的位置

              onPointerDown: (event) {
                print("手指按下 ${event.position}");
                print("手指按下 ${event.localPosition}");
              },
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F0Db0ByX-1586237096392)(EF10002EED8646F0AECB1860B7795D40)]

    现在这样就可以了

    这个0, 0还是不容易点到的

    这个就是我们的原始指针事件

    但是它的这个是事件比较少的

    比如我们想用长按 我们就不是很好做了

    我们可以改使用手势

    这个也是官方建议的

    2.2 手势识别的

    Gesture

    手势分成非常度多的种类

    Gesture分层非常多的种类:

    点击:

    • onTapDown:用户发生手指按下的操作
    • onTapUp:用户发生手指抬起的操作
    • onTap:用户点击事件完成
    • onTapCancel:事件按下过程中被取消

    双击:

    • onDoubleTap:快速点击了两次

    长按:

    • onLongPress:在屏幕上保持了一段时间

    纵向拖拽:

    • onVerticalDragStart:指针和屏幕产生接触并可能开始纵向移动;
    • onVerticalDragUpdate:指针和屏幕产生接触,在纵向上发生移动并保持移动;
    • onVerticalDragEnd:指针和屏幕产生接触结束;

    横线拖拽:

    • onHorizontalDragStart:指针和屏幕产生接触并可能开始横向移动;
    • onHorizontalDragUpdate:指针和屏幕产生接触,在横向上发生移动并保持移动;
    • onHorizontalDragEnd:指针和屏幕产生接触结束;

    移动:

    • onPanStart:指针和屏幕产生接触并可能开始横向移动或者纵向移动。如果设置了 onHorizontalDragStart 或者 onVerticalDragStart,该回调方法会引发崩溃;
    • onPanUpdate:指针和屏幕产生接触,在横向或者纵向上发生移动并保持移动。如果设置了 onHorizontalDragUpdate 或者 onVerticalDragUpdate,该回调方法会引发崩溃。
    • onPanEnd:指针先前和屏幕产生了接触,并且以特定速度移动,此后不再在屏幕接触上发生移动。如果设置了 onHorizontalDragEnd 或者 onVerticalDragEnd,该回调方法会引发崩溃。

    手势它的动作就非常的多

    我们这里列的就相对比较少 在源码里面差不多有50 个

    如何设置手势

    class HYHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: GestureDetector(
              child: Container(
                width: 200,
                height: 200,
                color: Colors.pink
              ),
            )
          ),
        );
      }
    }
    

    我们发现这个up 和 down 是有参数的

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6pWmT4wt-1586237096393)(503644AD56334BC995539247770D06E2)]

    class HYHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: GestureDetector(
              onTap: () {
                print("按下");
              },
              onTapDown: (event) {
                print("手势按下");
              },
              onTapUp: (event) {
                print("手势抬起");
              },
              onTapCancel: () {
                print("手势取消");
              },
              child: Container(
                width: 200,
                height: 200,
                color: Colors.pink
              ),
            )
          ),
        );
      }
    }
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jyg4BFP2-1586237096393)(001B15BFCEEB4C79BCC6FC16E822B46E)]

    但是如果我们想要拿到它的位置的话 我们可以

    它这里就科学的多了

              onTapDown: (detail) {
                print("手势按下");
                print(detail.globalPosition);
                print(detail.localPosition);
              },
              onTapUp: (detail) {
                print("手势抬起");
                print(detail.globalPosition);
                print(detail.localPosition);
              },
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-71X9PKy0-1586237096394)(6AF058AEE5584386BF595EA6F6F0F239)]

              onDoubleTap: () {
                print("手指双击");
              },
    

    这里和一些平台一样我们做长按的时候 它会取消掉 按下和抬起

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NVuKFdl2-1586237096394)(015F7C57FCC34CB6956E0E8FB7AB5B94)]

    但是长按没有

    它这里是

              onLongPress: () {
                print("手指长按");
              },
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nGvFdzZU-1586237096394)(CC3E7AA5A68E44C39D12043417919FDF)]

    import 'dart:async';
    
    import "package:flutter/material.dart";
    import 'package:learn_flutter02/extension/size_fit.dart';
    
    import "../extension/int_extension.dart";
    
    main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        HYSizeFit.initialize();
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          home: HYHomePage(),
        );
      }
    }
    
    class HYHomePage extends StatefulWidget {
      @override
      _HYHomePageState createState() => _HYHomePageState();
    }
    
    class _HYHomePageState extends State<HYHomePage> {
      int time = 0;
      bool flag = true;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: GestureDetector(
              onTap: () {
                print("按下");
              },
              onTapDown: (detail) {
                flag = true;
                time = 0;
                countTime();
                print("手势按下");
                print(detail.globalPosition);
                print(detail.localPosition);
              },
              onTapUp: (detail) {
                print("手势抬起");
                print(detail.globalPosition);
                print(detail.localPosition);
              },
              onTapCancel: () {
                print("手势取消");
              },
              onDoubleTap: () {
                print("手指双击");
              },
              onLongPress: () {
                flag = false;
                print("手指长按");
              },
              child: Container(
                width: 200,
                height: 200,
                color: Colors.pink
              ),
            )
          ),
        );
      }
    
      void countTime() {
        time++;
        if( flag && time < 500 ) {
          Timer(Duration(milliseconds: 1), () {
            print(time);
            countTime();
          });
        }
      }
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v7N0w0Rl-1586237096395)(C382183C0BF84AB690F92B50F00C2AFE)]

    这个长按的事件是100ms左右

    手势冒泡

    我们的这个冒泡的阻止 是通过一种间接的方式来实现的

    我们现在做一个东西 希望Container 嵌套 Container显示

    先做一个Container

    class HYHomePage extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Container(
              width: 200,
              height: 200,
              color: Colors.yellow
            )
          ),
        );
      }
    }
    

    然后我们希望在里面又嵌套一个Container

    class HYHomePage extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Container(
              width: 200,
              height: 200,
              color: Colors.yellow,
              child: Container(
                width: 100,
                height: 100, 
                color: Colors.red
              ),
            )
          ),
        );
      }
    }
    
    

    试一下呢

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t2I7Vysi-1586237096396)(7977D9FF12024DD5AEBDB10515FF3BCD)]

    结果变成全部红色的了

    回顾一下我们之前讲 Container的时候 它会使它的子组件填满它本身

    我们在Container包裹了一个Container的时候它会扩展到外面的Container

    如果我们想要做嵌套的话我们可以给他嵌套一个Column

    class HYHomePage extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Container(
              width: 200,
              height: 200,
              color: Colors.yellow,
              child: Column(
                children: <Widget>[
                  Container(
                    width: 100,
                    height: 100,
                    color: Colors.red
                  ),
                ],
              ),
            )
          ),
        );
      }
    }
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fZ6QRcRn-1586237096396)(6B4CC3902B714309AA51E26B4852205E)]

    还有一个办法就是设置一个alignment也是可以的

    class HYHomePage extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Container(
              width: 200,
              height: 200,
              color: Colors.yellow,
              alignment: Alignment.center,
              child: Container(
                width: 100,
                height: 100,
                color: Colors.red
              ),
            )
          ),
        );
      }
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pcuqgpho-1586237096397)(0172514FA6CD41478D8E044CE0804D30)]

    因为Container的源码里面如果你传了alignment的话它就会创建一个Align

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cgblvDW8-1586237096398)(B5C12063E2104ACE9D2A7CE15E328456)]

    如果这样的话我们就相当于包裹了一个Align

    开发里面如果要Container做嵌套的话我们就可以这样

    不用多嵌套了

    我们现在希望给这两个Container做一个手势

    但是我们希望我们里面Gesture不要传到外面

    所以之类我们就像整一个冒泡的阻止

    class HYHomePage extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: GestureDetector(
              onTapDown: (detail) {
                print("outter click");
              },
              child: Container(
                width: 200,
                height: 200,
                color: Colors.yellow,
                alignment: Alignment.center,
                child: GestureDetector(
                  onTapDown: (detail) {
                    print("inner click");
                  },
                  child: Container(
                    width: 100,
                    height: 100,
                    color: Colors.red
                  ),
                ),
              ),
            )
          ),
        );
      }
    }
    

    我们这样做一个东西

    我们发现它这里有点怪就是这个内部的点击事件

    偶尔会传到外面去

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tLkgE09A-1586237096399)(78344741810C46AB96670C3AC5DA8E37)]

    所以它偶尔会传到外面去

    所以我们希望彻底阻绝他们

    网上的话有些资料是说我们可以设置一些参数来阻止这个东西

    class HYHomePage extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: GestureDetector(
              onTapDown: (detail) {
                print("outter click");
              },
              child: Container(
                width: 200,
                height: 200,
                color: Colors.yellow,
                alignment: Alignment.center,
                child: GestureDetector(
                  behavior: HitTestBehavior.opaque,
                  onTapDown: (detail) {
                    print("inner click");
                  },
                  child: Container(
                    width: 100,
                    height: 100,
                    color: Colors.red
                  ),
                ),
              ),
            )
          ),
        );
      }
    }
    

    但是实际使用之后发现它还是不行

    Android好像就可以了

    所以有些时候不用太信奉网上的东西

    包括官方文档都是有可能错的 像是中文文档它翻译的时候经常就出问题

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f4i81jgz-1586237096399)(AC770A69953749C7914DBBB5D60D14EE)]

    针对于这种情况

    stackfloor上面有一个回答 获得了很多的星

    这两个东西就不要有嵌套关系

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mtma6xP5-1586237096400)(198BF5DDE2E24389AF8CB6051B28B591)]

    那我们有想做这样的界面但是它又要有嵌套关系

    我们可以使用Stack

    这里可以在最外层包裹(这里可以选择包裹一个Column然后再把它改成 Stack 因为Stack是没有快捷生成的 而Column生成出来子元素又是 children)

    class HYHomePage extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Stack(
              alignment: Alignment.center,
              children: <Widget>[
                GestureDetector(
                  onTapDown: (detail) {
                    print("outter click");
                  },
                  child: Container(
                    width: 200,
                    height: 200,
                    color: Colors.yellow,
                    alignment: Alignment.center,
                  ),
                ),
                GestureDetector(
                  behavior: HitTestBehavior.opaque,
                  onTapDown: (detail) {
                    print("inner click");
                  },
                  child: Container(
                      width: 100,
                      height: 100,
                      color: Colors.red
                  ),
                ),
              ],
            )
          ),
        );
      }
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2hcCu0Db-1586237096400)(183B16EF19B94726898E0E7A2B45DB24)]

    这样就不会出现问题了

    这个地方外面肯定是不会响应的

    那么我们又提一个需求 我们现在只希望 它响应外面的点击事件

    我们这里可以使用 IgnorePointer 来实现阻止内层冒泡

    class HYHomePage extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Stack(
              alignment: Alignment.center,
              children: <Widget>[
                GestureDetector(
                  onTapDown: (detail) {
                    print("outter click");
                  },
                  child: Container(
                    width: 200,
                    height: 200,
                    color: Colors.yellow,
                    alignment: Alignment.center,
                  ),
                ),
                IgnorePointer(
                  child: GestureDetector(
                    behavior: HitTestBehavior.opaque,
                    onTapDown: (detail) {
                      print("inner click");
                    },
                    child: Container(
                        width: 100,
                        height: 100,
                        color: Colors.red
                    ),
                  ),
                ),
              ],
            )
          ),
        );
      }
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KWjGxMct-1586237096401)(2C4609DFEBB74DE6B6BA92713DFC4DB4)]

    这样就可以了 相当于它用了一种的另类的方法来实现阻止冒泡

    事件总线(跨组件事件的传递)

    比如说我们现在有两个widget

    我们想要在两个隔的很远的widget进行通讯

    有可能同页但是隔了很远 还有可能不同页

    但是我们还是希望能做出响应

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TPafFkCB-1586237096401)(7C8EE88693174398A190E5A2B95A1F6E)]

    如果这样的话我们可能就需要被响应位置的传对应的回调函数 隔很多层将这个回调函数传过来

    • 如果不想这样做的话我们怎么做呢

    这个时候我们就可以使用eventbus事件总线

    之前我们没有这个东西 所以自己要搞的话就要需要一个Map<String, List>

    如果你要监听对应的String的事件 你就可以把这个回调函数加到哪里

    一旦我们发出事件的时候我们就把对应的事件取出来

    然后我们就会调用对应的Callback 但是它这里需要传一个泛型 如果不传你就不知道它里面的返回的东西是什么

    这里有人已经写好了一个事件总线我们直接用就是了

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kEFNRspg-1586237096402)(C6CB6C3DBEBE40F488B8FE4A20558B3E)]

    这个东西就叫event_bus

    它这个东西是基于dart Streams来实现的

    dart里面有很多的语法的 我们学习了基础的语法以后 我们掌握这些语法其实还比较容易的

    我们现在有这样的一个需求

    我们建立这样的一个widget 我们希望能点击button以后不在同一个层级的Text能发生改变

    就希望这里可以发生一个通讯 你学会 了这个东西我们其他的地方也是一样的

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R7NbNT4n-1586237096402)(108FFDDBFD46473DA1AE7E40F1CB78FC)]

    我们建立下面的Text最好选择fulWidget因为我们需要改变里面的值所以最好选择可以记录状态的东西

    import "package:flutter/material.dart";
    
    main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          home: HYHomePage(),
        );
      }
    }
    
    class HYHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                HYButton(),
                HYText()
              ],
            ),
          ),
        );
      }
    }
    
    class HYButton extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return RaisedButton(
          child: Icon(Icons.add_a_photo),
          onPressed: () {
            
          },
        );
      }
    }
    
    class HYText extends StatefulWidget {
      @override
      _HYTextState createState() => _HYTextState();
    }
    
    class _HYTextState extends State<HYText> {
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Text("文本"),
        );
      }
    }
    
    
    

    我们的Text 和 button互为兄弟组件

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MJtrHhe0-1586237096403)(1A58586706F8406D9A8466664E6539B7)]

    所以如果是现在这我们可能需要拿到 Text 然后再拿到里面的回调函数

    让后让这个Text做一个改变 这里我们可能就需要globalKey 了

    import "package:flutter/material.dart";
    
    main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          home: HYHomePage(),
        );
      }
    }
    
    class HYHomePage extends StatelessWidget {
      final GlobalKey<_HYTextState> homeKey = GlobalKey<_HYTextState>();
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                HYButton(homeKey),
                HYText(key: homeKey)
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.change_history),
            onPressed: () {
              homeKey.currentState.setString();
            },
          ),
        );
      }
    }
    
    class HYButton extends StatelessWidget {
      final GlobalKey<_HYTextState> tempKey;
    
      HYButton(this.tempKey);
    
      @override
      Widget build(BuildContext context) {
        return RaisedButton(
          child: Icon(Icons.add_a_photo),
          onPressed: () {
            tempKey.currentState.setString();
          },
        );
      }
    }
    
    class HYText extends StatefulWidget {
      HYText({Key key}):super(key: key);
    
      @override
      _HYTextState createState() => _HYTextState();
    }
    
    class _HYTextState extends State<HYText> {
      String _text = "文本";
    
      void setString() {
        setState(() {
          _text = "改版的文本";
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Text(_text),
        );
      }
    }
    
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AmrdEr1c-1586237096403)(4E4FE2C7967347FA8F921A4BEC79CE9A)]

    这样就可以了

    这里还是可以这样做 但是如果你层级太多你就不好做了

    所以我们这里就可以使用时间总线

    首先它是一个第三方的东西

    这个东西的用法是分成三步的

    1. 创建全局的event_bus对象

    在全局的地方创建这个event_bus

    import 'package:event_bus/event_bus.dart';
    import "package:flutter/material.dart";
    
    main() => runApp(MyApp());
    
    final eventBus = EventBus();
    
    1. 在对应的位置发出事件啊
    class HYButton extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return RaisedButton(
          child: Icon(Icons.add_a_photo),
          onPressed: () {
            eventBus.fire("你好啊 李银河");
          },
        );
      }
    }
    
    1. 监听发出的事件

    它这里是靠事件的类型来监听的 所以 我们在监听的时候 一般不会使用这个String来作为发出或者监听的类型

    就算是要发出String也是会包装一个对象

    这个每次发出事件的时候 都最好定义一个模型 官方说是定义一个event

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1hIghG46-1586237096404)(4149FC2C6E4746CAAFEC43A5BA9656F8)]

    class HYText extends StatefulWidget {
    
      @override
      _HYTextState createState() => _HYTextState();
    }
    
    class _HYTextState extends State<HYText> {
      String _message = "hello world";
    
      @override
      void initState() {
        // TODO: implement initState
        super.initState();
    
    //    这里我们一般开发的时候我们不会监听这个String类型的东西
        eventBus.on<String>().listen((data) {
          print(data);
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Text(_message),
        );
      }
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oASuB5np-1586237096404)(D135D0C39F404C6FB4C5F2951D4C87A3)]

    这样就可以了

    一般我们会把它整成一个类 一个model 这个model里面就给他保存一些数据

    然后发出这个对象的

    import 'package:event_bus/event_bus.dart';
    import "package:flutter/material.dart";
    
    main() => runApp(MyApp());
    
    final eventBus = EventBus();
    
    class UserInfo {
      String nickName;
      int age;
    
      UserInfo(this.nickName, this.age);
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          home: HYHomePage(),
        );
      }
    }
    
    class HYHomePage extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                HYButton(),
                HYText()
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.change_history),
            onPressed: () {
            },
          ),
        );
      }
    }
    
    class HYButton extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return RaisedButton(
          child: Icon(Icons.add_a_photo),
          onPressed: () {
            UserInfo user = UserInfo("nick", 23);
            eventBus.fire(user);
          },
        );
      }
    }
    
    class HYText extends StatefulWidget {
    
      @override
      _HYTextState createState() => _HYTextState();
    }
    
    class _HYTextState extends State<HYText> {
      String _message = "hello world";
    
      @override
      void initState() {
        // TODO: implement initState
        super.initState();
    
    //    这里我们一般开发的时候我们不会监听这个String类型的东西
        eventBus.on<UserInfo>().listen((data) {
          print("name: ${data.nickName} age: ${data.age}");
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Text(_message),
        );
      }
    }
    
    
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8GmLW9cQ-1586237096405)(00B353112CC14ABDADB75D8F990003F7)]

    import 'package:event_bus/event_bus.dart';
    import "package:flutter/material.dart";
    
    main() => runApp(MyApp());
    
    final eventBus = EventBus();
    
    class UserInfo {
      String nickName;
      int age;
    
      UserInfo(this.nickName, this.age);
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          home: HYHomePage(),
        );
      }
    }
    
    class HYHomePage extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                HYButton(),
                HYText()
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.change_history),
            onPressed: () {
            },
          ),
        );
      }
    }
    
    class HYButton extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return RaisedButton(
          child: Icon(Icons.add_a_photo),
          onPressed: () {
            UserInfo user = UserInfo("nick", 23);
            eventBus.fire(user);
          },
        );
      }
    }
    
    class HYText extends StatefulWidget {
    
      @override
      _HYTextState createState() => _HYTextState();
    }
    
    class _HYTextState extends State<HYText> {
      String _message = "hello world";
    
      @override
      void initState() {
        // TODO: implement initState
        super.initState();
    
    //    这里我们一般开发的时候我们不会监听这个String类型的东西
        eventBus.on<UserInfo>().listen((data) {
          print("name: ${data.nickName} age: ${data.age}");
          setState(() {
            _message = "name: ${data.nickName} age:  ${data.age}";
          });
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Container(
          child: Text(_message),
        );
      }
    }
    
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mt7cA8xt-1586237096406)(B98B500C3F59494EBFEB612BEE53C245)]

    这样就可以了

    开发当中我们用这个东西就会更方便

    • 所以我们每次用的时候就需要给他定义一个event模型

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vvOD9i9W-1586237096407)(567C1241E2104C82814499466A655213)]

    • 还有一个问题后面我们分文件的时候我们需要分开 这个东西怎么传

    我们可以用文件专门用event_bus

    这样我们就可以在不同的文件里面使用这个这个事件总线了

    event_bus.dart

    import 'package:event_bus/event_bus.dart';
    
    final eventBus = EventBus();
    
    import "package:flutter/material.dart";
    
    import "event_bus.dart";
    
    main() => runApp(MyApp());
    
    

    这个就是一个模块的导入

    这个event_bus它是一个全局的对象 一般是不会销毁它

    路由和导航

    之前我们用indexStacked实现类似的功能但是

    我们如果想要在这个上面添加详情页的功能的话 就要使用到路由了

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AGFxS89z-1586237096408)(5A7E6F641D354C6F9E32F12783B17BA9)]

    跳转到右下的详情页 你就要通过类似vue之类东西的push的方式

    如果你要这样的话

    2.1. 认识Flutter路由

    路由的概念由来已久,包括网络路由、后端路由,到现在广为流行的前端路由。

    • 无论路由的概念如何应用,它的核心是一个路由映射表
    • 比如:名字 detail 映射到 DetailPage 页面等
    • 有了这个映射表之后,我们就可以方便的根据名字来完成路由的转发(在前端表现出来的就是页面跳转)

    在Flutter中,路由管理主要有两个类:Route和Navigator

    这个路由有很多 前端路由 后端路由

    这种单页面复应用, spa页面它们都是用的单页面复应用 都是用的前端路由

    为什么它叫前端路由可以去看我的vue的路由的部分

    我们的这个东西 它其实就是像一个map的东西 一个detail就对应一个页面

    这个东西就是一个路由表一样的东西

    和路由器里面的东西是一样的

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-afCDVhRE-1586237096409)(6C73A5A0FE1245E0B4715D911D375614)]

    这个就是一个 路由表

    在flutter如果你像管理这个路由的话 我们主要是用到两个类

    Route 和 Navigator

    一个页面要想被路由我们就要包装到一个路由里面

    到时候我们的Navigation才能帮助我们管理 页面

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iGfk2LdX-1586237096409)(CCF9A8EC18DC4A518A797D22A820FA8C)]

    如果我们有很多的路由我们就可以把它放到一个表里面来管理

    表就是路由表

    2.2. Route

    Route:一个页面要想被路由统一管理,必须包装为一个Route

    • 官方的说法很清晰:An abstraction for an entry managed by a Navigator.

    你是一个抽象的路由它是一个入口被Navigation来管理

    但是Route是一个抽象类,所以它是不能实例化的

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gXKnNb59-1586237096410)(6FAB26E02BC04BCBA852AE20558A4B81)]

    我们来看一下这个它有很多的子类

    但是我们常用的一般就会直接写在这里顶上面

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UnpeKx4f-1586237096411)(6781EDA013254911A57720E0FCCF09E6)]

    它希望我们用这个MaterialPageRoute来替代 屏幕的入口

    我们就可以对这个东西来做一个实例化

    /// See [MaterialPageRoute] for a route that replaces the
    /// entire screen with a platform-adaptive transition.
    

    MaterialPageRoute就是很多 Route功能的综合

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VwHAcaGs-1586237096411)(1B41F5FCD9004F92A16C37CC0CCCB7FB)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e8vTkANO-1586237096412)(46F993F09F584443B571D55FFBA585FB)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-76a4ywdE-1586237096412)(CA77178E6CB8445CA96E1600A4D5FC4F)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EcWykF2B-1586237096413)(E49D0A8CF59641EFBA6BF99792BBE938)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KcASRpgf-1586237096414)(79D7F99309464F6CB3810E1DA476BBB5)]

    也就是说它有这些东西的功能

    所以以后我们在做跳转的时候我们就会给他包裹一个MaterialPageRoute用的是最多的

    • 表现

    Android 是从底部画出来的

    关闭是从上面到限免

    IOS是从做左到右

    当然我们也有办法在Android上用IOS的切换模式

    用它也是可以的

    2.3 Navigator

    Navigator: 管理里面的Route的Widget, 通过一个Stack来进行管理的

    • 官方的说法也很清晰:A widget that manages a set of child widgets with a stack discipline.

    也就是说先进后出

    • 但是我们是不用直接创建Navigator 因为这个对象它MaterialApp、CupertinoApp、WidgetsApp它们默认是有插入Navigator的
    • 但是我们是不用去直接创建的

    这个对象是通过InheritedWidget的方式来管理的

    我们可以看到里面有一个of方法

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bltDxGQo-1586237096415)(E283E3C642BF4DEAAC2D3AEFD35D7789)]

    可以看到它先是拿到一个 rootNavigator 然后看里面有没有值

    如果没有值就去里面查找 虽然这个和之前的那个名字不一样

    但是本质上都是沿着Element树 去查找对应的Navigator

    不过它这里是用的State的方式来查找的

    其实这里就因该是找到自动创建的那个Navigator对象

    同样需要传递一个context 拿到这个Navigator我们就可以跳转到对应的页面了

        Navigator.of(context).pushNamed("");
    
    • Navigator几个最常用的方法
    1. push: 这个方法是让我们传入一个Route
    2. pushNamed: 是传入的是要给名字
    3. pop:这个就是弹出栈
    • 使用

    我们创建一个跳转的页面

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rA04fQ3c-1586237096416)(662E1A71B2EB4185B0A0D2A0C41AD486)]

    import "package:flutter/material.dart";
    
    class HYDetailScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("detail")
          ),
          body: Center(
            child: Text("测试"),
          ),
        );
      }
    }
    
    

    然后回来

    这里我们使用

    跳转都可以 那个省略了of就是在这个函数里面把它放到一起了

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UE69ka8N-1586237096417)(09E2A67A5292482CBC2B26408DB48D73)]

        Navigator.of(context).push(route);
        Navigator.push(context, route)
    

    但是我们跳转的时候 不能直接用HYDetailScreen这样的形式进行跳转

    我们需要把它封装到一个route里面

    我们就需要创建一个route

    import "package:flutter/material.dart";
    import 'package:learn_flutter02/day11_route_Protice/detail.dart';
    
    main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          home: HYHomePage(),
        );
      }
    }
    
    class HYHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: RaisedButton(
              child: Text("跳转到详情页面"),
              onPressed: () => _jumpToDetail(context),
            ),
          ),
        );
      }
    
      void _jumpToDetail(context) {
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (BuildContext context) {
              return HYDetailScreen();
            }
          )
        );
      }
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pHzOtl6w-1586237096417)(46161F72A5094905A2423FA9588964B9)]

    我们现在是点击上面的那个东西返回

    那我们现在希望点击一个按钮然后返回

    import "package:flutter/material.dart";
    
    class HYDetailScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("detail")
          ),
          body: Center(
            child: Column(
              children: <Widget>[
                Text("测试"),
                RaisedButton(
                  child: Text("回到首页"),
                  onPressed: () => _backToHome(context),
                )
              ],
            ),
          ),
        );
      }
    
      void _backToHome(BuildContext context) {
        print("回到首页");
        Navigator.of(context).pop();
      }
    }
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-th34Gj3K-1586237096418)(6175A986D4F54986B40BAC96BF568C8B)]

    另外的跳转方式

    我们这里的跳转时方式是 用了一个MaterialPageRoute然后来做的一个跳转

    • 我们还希望能获得参数

    这样的话我们如果可以获得一个商品的id那我们就可以对应的请求数据

    import "package:flutter/material.dart";
    import 'package:learn_flutter02/day11_route_Protice/detail.dart';
    
    main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          home: HYHomePage(),
        );
      }
    }
    
    class HYHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: RaisedButton(
              child: Text("跳转到详情页面"),
              onPressed: () => _jumpToDetail(context),
            ),
          ),
        );
      }
    
      void _jumpToDetail(context) {
    //    1. 这个是要给普通的跳转方式
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (BuildContext context) {
              return HYDetailScreen("a home message");
            }
          )
        );
      }
    }
    
    import "package:flutter/material.dart";
    
    class HYDetailScreen extends StatelessWidget {
      final String _message;
    
      HYDetailScreen(this._message);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("detail")
          ),
          body: Center(
            child: Column(
              children: <Widget>[
                Text(_message, style: TextStyle(fontSize: 30)),
                RaisedButton(
                  child: Text("回到首页"),
                  onPressed: () => _backToHome(context),
                )
              ],
            ),
          ),
        );
      }
    
      void _backToHome(BuildContext context) {
        print("回到首页");
        Navigator.of(context).pop();
      }
    }
    
    

    这种一般的路由我们可以通过构造器来传递参数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qJV0KYOL-1586237096418)(E5B9EB01DFB840719C3895F2C00211FF)]

    我们返回的时候 也有可能会想要传递对应的参数

    我们的pop也是有一个可选参数的

    detail.dart

      void _backToHome(BuildContext context) {
        print("回到首页");
        Navigator.of(context).pop(" a detial message");
      }
    

    但是有个问题就是我们返回了 你怎么拿到这个东西

    这个东西多半只能用回调来拿

    我们发现这个东西它返回的是一个Future

       void _jumpToDetail(context) {
    //    1. 这个是要给普通的跳转方式
        Future res =  Navigator.of(context).push(
          MaterialPageRoute(
            builder: (BuildContext context) {
              return HYDetailScreen("a home message");
            }
          )
        );
        res.then((result) {
          print("首页获得的参数 $result");
        });
      }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5cko9oU3-1586237096419)(670E9980F948466DBA77C25EFDD61F03)]

    它这里返回了一个Future 所以

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UqhIHlxP-1586237096419)(3A943F984C2A4976A27AFFF1FDC91FFA)]

    main.dart

    class HYHomePage extends StatefulWidget {
      @override
      _HYHomePageState createState() => _HYHomePageState();
    }
    
    class _HYHomePageState extends State<HYHomePage> {
      String message  = "home message";
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Column(
              children: <Widget>[
                RaisedButton(
                  child: Text("跳转到详情页面"),
                  onPressed: () => _jumpToDetail(context),
                ),
                Text(message)
              ],
            ),
          ),
        );
      }
    
      void _jumpToDetail(context) {
    //    1. 这个是要给普通的跳转方式
        Future res =  Navigator.of(context).push(
          MaterialPageRoute(
            builder: (BuildContext context) {
              return HYDetailScreen("a home message");
            }
          )
        );
        res.then((result) {
          print("首页获得的参数 $result");
          message = result;
        });
      }
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TbeVurcR-1586237096420)(4234E853CC8C4A548777218ED032ED3E)]

    但是你有没有考虑过我们使用它默认的坐上的按钮也要返回数据

    如果不这样

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DL1n5t8p-1586237096420)(9FD8D0D02D824CCEBA557BCF4EAC17DE)]

    就会报错因为这里我们把一个null赋值给了一个字符串变量

    所以这里我们返回的时候就有问题了

    所以这里我们希望我们再点返回按钮的时候它也要携带一些返回的信息

    这个东西我们用两个东西来做 默认情况下 它是默认来监听的

    我们找到我们的AppBar

    1. 使用appBar里面去自己定义一个 返回按钮

    它有一个属性leading

    所以我们修改一下

    import "package:flutter/material.dart";
    
    class HYDetailScreen extends StatelessWidget {
      final String _message;
    
      HYDetailScreen(this._message);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("detail"),
            leading: IconButton(
              icon: Icon(Icons.arrow_back),
              onPressed: () {
                Navigator.of(context).pop(" a detial message");
              },
            ),
          ),
          body: Center(
            child: Column(
              children: <Widget>[
                Text(_message, style: TextStyle(fontSize: 30)),
                RaisedButton(
                  child: Text("回到首页"),
                  onPressed: () => _backToHome(context),
                )
              ],
            ),
          ),
        );
      }
    
      void _backToHome(BuildContext context) {
        print("回到首页");
        Navigator.of(context).pop(" a detial message");
      }
    }
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OQv7uVAW-1586237096421)(181D0B531F0A40F18E798871EDC2F25C)]

    1. 但是如果我们现在就想监听它原来的这个按钮的点击

    这个东西就会麻烦一点

    我们需要给这个Scaffold包裹一个Widget WillPopScope

    它有两个必传的参数 child 就是Scaffold 和 onWillPop这个东西

    import "package:flutter/material.dart";
    
    class HYDetailScreen extends StatelessWidget {
      final String _message;
    
      HYDetailScreen(this._message);
    
      @override
      Widget build(BuildContext context) {
        return WillPopScope(
          onWillPop: () {
    //        这里写true 写false是有区别的
            return Future.value(true);
          },
          child: Scaffold(
            appBar: AppBar(
              title: Text("detail"),
              leading: IconButton(
                icon: Icon(Icons.arrow_back),
                onPressed: () {
                  Navigator.of(context).pop(" a detial message");
                },
              ),
            ),
            body: Center(
              child: Column(
                children: <Widget>[
                  Text(_message, style: TextStyle(fontSize: 30)),
                  RaisedButton(
                    child: Text("回到首页"),
                    onPressed: () => _backToHome(context),
                  )
                ],
              ),
            ),
          ),
        );
      }
    
      void _backToHome(BuildContext context) {
        print("回到首页");
        Navigator.of(context).pop(" a detial message");
      }
    }
    
    

    这个东西为true的时候给他是可以自动返回的

    flutter 帮助我们执行返回操作

          onWillPop: () {
    //        这里写true 写false是有区别的
            return Future.value(true);
          },
    

    当我们为false的时候

          onWillPop: () {
    //        这里写true 写false是有区别的
            return Future.value(false);
          },
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ajAZfF11-1586237096422)(3068070DB1C64459AB7D8E53C2219F2A)]

    就是自行写返回代码

          onWillPop: () {
            _backToHome(context);
    //        这里写true 写false是有区别的
            return Future.value(false);
          },
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xIeaLswp-1586237096423)(1420BAD567FE489EBAFD167374035EC9)]

    这样就可以做一个监听

    这个就是路由的跳转 和 参数的传递

    路由表

    为什么要有路由表

    我们现在如果要在多个页面都使用一个页面

    我们要在多个页面都需要这个详情页

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sd2W4pIT-1586237096423)(6A04E6D717D041C0814A24252B588E6B)]

    那么我们就需要在每个地方都写这样一大段代码

      void _jumpToDetail(context) {
    //    1. 这个是要给普通的跳转方式
        Future res =  Navigator.of(context).push(
          MaterialPageRoute(
            builder: (BuildContext context) {
              return HYDetailScreen("a home message");
            }
          )
        );
      }
    

    这样就太麻烦了

    所以这里我们就可以使用路由映射表

    我们可以给它搞一个名字 让他映射一个页面

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mm7p8BZH-1586237096424)(F1870B55C0AA49489167329AACB3586C)]

    后面我们在用的时候我们就用名字来进行跳转就可以了

    我们整一个页面

    about.dart

    import "package:flutter/material.dart";
    
    class HYAboutPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("关于页"),
          ),
          body: Center(
            child: Column(
              children: <Widget>[
                Text("1111"),
                RaisedButton(
                  child: Text("跳转"),
                  onPressed: () {
    
                  },
                )
              ],
            ),
          ),
        );
      }
    }
    
    

    然后在

    main.dart里面跳转

    class _HYHomePageState extends State<HYHomePage> {
      String message  = "default";
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Column(
              children: <Widget>[
                RaisedButton(
                  child: Text("跳转到详情页面"),
                  onPressed: () => _jumpToDetail(context),
                ),
                RaisedButton(
                  child: Text("跳转到关于页面页面"),
                  onPressed: () => _jumpToAbout(context),
                ),
                Text(message, style: TextStyle(fontSize: 20))
              ],
            ),
          ),
        );
      }
    
      void _jumpToDetail(context) {
    //    1. 这个是要给普通的跳转方式
        Future res =  Navigator.of(context).push(
          MaterialPageRoute(
            builder: (BuildContext context) {
              return HYDetailScreen("a home message");
            }
          )
        );
        res.then((result) {
          print("首页获得的参数 $result");
          message = result;
        });
      }
    
      void _jumpToAbout(BuildContext context) {
        Navigator.of(context).pushNamed("/about");
      }
    }
    

    Navigator.of(context).pushNamed("/about"); 这个地方就可以进行跳转

    当然我们这里没有配置这个 /about 所以也就没有办法 跳转

    我们来到MyApp

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          routes: ,
          home: HYHomePage(),
        );
      }
    }
    

    这个MaterialApp可以传一个参数routes很明显它是要传很多的路由

    所以路由表多半就是在这里传了

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tiuNJUw6-1586237096425)(7C9C989802CC41679229BD2B61F1C7A4)]

    它这里要传的参数是一个map 所以就传一个{}

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          routes: {
            "/about": (ctx) => HYAboutPage()
          },
          home: HYHomePage(),
        );
      }
    }
    
    

    这样我们就把这个about的路由映射配置好了

    那我们的首页 也是可以之直接写在这里的

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          routes: {
            "/": (ctx) => HYHomePage(),
            "/about": (ctx) => HYAboutPage()
          },
          home: HYHomePage(),
        );
      }
    }
    

    之前我们的Material里面是有一个home属性的

    这个东西我们都是可以不写的

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          routes: {
            "/": (ctx) => HYHomePage(),
            "/about": (ctx) => HYAboutPage()
          },
          initialRoute: "/",
        );
      }
    }
    

    这样我们就可以 默认启动homepage的页面了

    所以我们的代码可以这样来写

    我们来试一下看看其他的路由跳转能否实现

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aS7pbdkX-1586237096425)(C48EFFEE1E1149DB83EF0E8196E3E467)]

    跳转回来也很容易

    import "package:flutter/material.dart";
    
    class HYAboutPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("关于页"),
          ),
          body: Center(
            child: Column(
              children: <Widget>[
                Text("1111"),
                RaisedButton(
                  child: Text("跳转"),
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                )
              ],
            ),
          ),
        );
      }
    }
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ACDhnTfS-1586237096426)(3872B1E0718B40A382839C6BED4CC867)]

    但是我们这里 很多的地方我们都使用了很多的字符串常量 就是那个我们用于跳转的地方 路由的地址

    我们很多的地方 都有这个东西 我们很容易写错这个东西

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JDYpwYXv-1586237096426)(A0EA6F7669254F05966FDE1C47F3A2B0)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FrS6piJE-1586237096427)(580EE490430D4FE0ADE000CDB4053471)]

    虽然来说还是不容易写错但是 你还是不能保证他一定不会出错

    所以那我们在开发的时候 我们会把这个东西定义成常量然后放到一个文件里面

    我们就会在对应的页面里面 写一个静态的变量来保存这个 路由地址的变量这样 有系统检错 就不容易出错

    about.dart

    class HYAboutPage extends StatelessWidget {
      static final String routeName = "/about";
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
    

    然后使用的时候我们

    main.dart

    ...
          routes: {
            "/": (ctx) => HYHomePage(),
            HYAboutPage.routeName: (ctx) => HYAboutPage()
          },
    ...
      void _jumpToAbout(BuildContext context) {
        Navigator.of(context).pushNamed(HYAboutPage.routeName);
      }
    }
    

    同样main这些路由我们都可以这样配置

    首页的路由我们也可以这样配置

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          routes: {
            HYHomePage.routeName: (ctx) => HYHomePage(),
            HYAboutPage.routeName: (ctx) => HYAboutPage()
          },
          initialRoute: "/",
        );
      }
    }
    
    class HYHomePage extends StatefulWidget {
      static final String routeName = "/";
    
    

    这种就是开发里面约定俗成的代码规范

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4I4HhsOM-1586237096428)(35097C9C952C4D7BB93EEEDB87DE261C)]

    如果我们通过这种pushNamed 需要传递一些参数 那么我们要怎么传呢

    那这个参数怎么传呢

    我们之前是通过构造器来传递参数的 但是现在已经不能通过构造器来传递参数了

    更根本都没有调用 构造函数

    • 我们可以通过这个arguments 来拿到这个参数了
      void _jumpToAbout(BuildContext context) {
        Navigator.of(context).pushNamed(HYAboutPage.routeName, arguments: "a home message PushNamed");
      }
    

    那么我们怎么拿到这个数据呢

    我们可以通过

    ModalRoute.of(context).setting.arguments拿到对应的参数

    如果你确定他的类型的话你还可以转化过来

    Modal.of(context).setting.arguments as String 拿到这个参数

    这个东西他默认情况下获得的是要给Object类型

    你想要转化的话 还需要as String 来转化

    about.dart

    import "package:flutter/material.dart";
    
    class HYAboutPage extends StatelessWidget {
      static final String routeName = "/about";
    
      @override
      Widget build(BuildContext context) {
        //    如果你确定这个东西是一个String类型的东西 我们就可以通过这个方式来拿到对应的参数
        final message = ModalRoute.of(context).settings.arguments as String;
    
        return Scaffold(
          appBar: AppBar(
            title: Text("关于页"),
          ),
          body: Center(
            child: Column(
              children: <Widget>[
                Text(message),
                RaisedButton(
                  child: Text("跳转"),
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                )
              ],
            ),
          ),
        );
      }
    }
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TNzguJdJ-1586237096428)(A885DAE81C134946846BDFF9FCD13AF1)]

    这个东西就是我们的命名路由

    也是我们的命名路由怎么传递参数

    他怎么返回呢

    一样的 这个方法也会返回 Future

    import "package:flutter/material.dart";
    import 'package:learn_flutter02/day11_route_Protice/about.dart';
    import 'package:learn_flutter02/day11_route_Protice/detail.dart';
    
    main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          routes: {
            HYHomePage.routeName: (ctx) => HYHomePage(),
            HYAboutPage.routeName: (ctx) => HYAboutPage()
          },
          initialRoute: "/",
        );
      }
    }
    
    class HYHomePage extends StatefulWidget {
      static final String routeName = "/";
    
      @override
      _HYHomePageState createState() => _HYHomePageState();
    }
    
    class _HYHomePageState extends State<HYHomePage> {
      String message  = "default";
    
      @override
      Widget build(BuildContext context) {
    
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                RaisedButton(
                  child: Text("跳转到详情页面"),
                  onPressed: () => _jumpToDetail(context),
                ),
                RaisedButton(
                  child: Text("跳转到关于页面页面"),
                  onPressed: () => _jumpToAbout(context),
                ),
                Text(message, style: TextStyle(fontSize: 20))
              ],
            ),
          ),
        );
      }
    
      void _jumpToDetail(context) {
    //    1. 这个是要给普通的跳转方式
        Future res =  Navigator.of(context).push(
          MaterialPageRoute(
            builder: (BuildContext context) {
              return HYDetailScreen("a home message");
            }
          )
        );
        res.then((result) {
          print("首页获得的参数 $result");
          message = result;
        });
      }
    
      void _jumpToAbout(BuildContext context) {
        Future resFuture = Navigator.of(context).pushNamed(HYAboutPage.routeName, arguments: "a home message PushNamed");
        resFuture.then((res) {
          print("获得传递的参数值:$res");
        });
      }
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MLLbfLkO-1586237096429)(32ADF431913845F3AEA656C8CC2FE068)]

    这个代码是一样的 这个 就是命名路由传递参数的方式

    • 将Detail也使用命名路由的方式跳转

    那简单啊

    但是 我们之前是给这个Detail写了一个构造函数的 这里参数还不能乱传

    所以对于我们的Detail就不能用命名路由的方式进行跳转

    你想要通过命名路由的方式跳转 就不行

    当然如果我们把detail修改了 写的和about一样没有带参构造

    那肯定是可以的

    但是我们希望在不改变原来的基础上 设置这个路由

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0uPJrQcA-1586237096429)(E6CAD2FD14604211A2DEB4C7F9673BC2)]

    我们可以在这里找到一个钩子函数 当你的路由在这里找不到的时候

    onGenerateRoute

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5VqXqBhq-1586237096430)(999DFF993378468A8D7087F7F9A5ABBD)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nK6y2psg-1586237096432)(E171FE099A764140BE6DD3083D77115C)]

    可以看到它需要传递的参数 然后要返回一个Route 但是我们这里又不是只有一个路由

    你得根据不同的名字返回不同的页面 所以需要判断

          onGenerateRoute: (setting) {
            if( setting.name == HYDetailScreen.routeName ) {
              return MaterialPageRoute(
                builder: (ctx) {
                  return HYDetailScreen();
                }
              );
            }
          },
    

    但是不对啊 这里还是有参数

    那你就跟参数啊

      void _jumpToAbout(BuildContext context) {
        Future resFuture = Navigator.of(context).pushNamed(HYAboutPage.routeName, arguments: "a home message PushNamed");
        resFuture.then((res) {
          print("获得传递的参数值:$res");
        });
      }
    
      void _jumpToDetail2(BuildContext context) {
    //    按以前的方式 我们就这样
        Navigator.of(context).pushNamed(HYDetailScreen.routeName, arguments: "a home Detail2 Message");
      }
    }
    

    同样传递这个参数 然后拿到对应的参数

          onGenerateRoute: (settings) {
            if( settings.name == HYDetailScreen.routeName ) {
              return MaterialPageRoute(
                builder: (ctx) {
                  return HYDetailScreen(settings.arguments);
                }
              );
            }
            return null;
          },
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LkUFHuB0-1586237096433)(AAB62767306A43428C1A479978157121)]

    这个东西就可以使用带有构造函数参数 不改变结构的情况下使用 命名路由

    import "package:flutter/material.dart";
    import 'package:learn_flutter02/day11_route_Protice/about.dart';
    import 'package:learn_flutter02/day11_route_Protice/detail.dart';
    
    main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          routes: {
            HYHomePage.routeName: (ctx) => HYHomePage(),
            HYAboutPage.routeName: (ctx) => HYAboutPage(),
          },
          initialRoute: "/",
          onGenerateRoute: (settings) {
            if( settings.name == HYDetailScreen.routeName ) {
              return MaterialPageRoute(
                builder: (ctx) {
                  return HYDetailScreen(settings.arguments);
                }
              );
            }
            return null;
          },
        );
      }
    }
    
    class HYHomePage extends StatefulWidget {
      static final String routeName = "/";
    
      @override
      _HYHomePageState createState() => _HYHomePageState();
    }
    
    class _HYHomePageState extends State<HYHomePage> {
      String message  = "default";
    
      @override
      Widget build(BuildContext context) {
    
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                RaisedButton(
                  child: Text("跳转到详情页面"),
                  onPressed: () => _jumpToDetail(context),
                ),
                RaisedButton(
                  child: Text("跳转到关于页面页面"),
                  onPressed: () => _jumpToAbout(context),
                ),
                RaisedButton(
                  child: Text("跳转到详情页面"),
                  onPressed: () => _jumpToDetail2(context),
                ),
                Text(message, style: TextStyle(fontSize: 20))
              ],
            ),
          ),
        );
      }
    
      void _jumpToDetail(context) {
    //    1. 这个是要给普通的跳转方式
        Future res =  Navigator.of(context).push(
          MaterialPageRoute(
            builder: (BuildContext context) {
              return HYDetailScreen("a home message");
            }
          )
        );
        res.then((result) {
          print("首页获得的参数 $result");
          message = result;
        });
      }
    
      void _jumpToAbout(BuildContext context) {
        Future resFuture = Navigator.of(context).pushNamed(HYAboutPage.routeName, arguments: "a home message PushNamed");
        resFuture.then((res) {
          print("获得传递的参数值:$res");
        });
      }
    
      void _jumpToDetail2(BuildContext context) {
    //    按以前的方式 我们就这样
        Navigator.of(context).pushNamed(HYDetailScreen.routeName, arguments: "a home Detail2 Message");
      }
    }
    
    • 错误页面的设置

    如果我们这里压根都没有这个页面跳转会怎么样呢

                RaisedButton(
                  child: Text("跳转到设置页面"),
                  onPressed: () => _jumpToSetting(context),
                ),
    ...
    
    ...
      void _jumpToSetting(BuildContext context) {
        Navigator.of(context).pushNamed("setting");
      }
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CIJmV8YN-1586237096433)(C128BBD6B4AE4A9F804D15168730EBC9)]

    它报错了

    它这里直接就报错了 如果确实有这种情况 我们希望它报错吗 不希望 我们希望它跳转到一个错误页面

    我们可以定制一个错误页面

    error.dart

    import "package:flutter/material.dart";
    
    class HYErrorScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("error")
          ),
          body: Center(
            child: Text("错误页面", style: TextStyle(fontSize: 20))
          ),
        );
      }
    }
    
    

    这个东西怎么配置呢 它就需要另外一个钩子函数了

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          routes: {
            HYHomePage.routeName: (ctx) => HYHomePage(),
            HYAboutPage.routeName: (ctx) => HYAboutPage(),
          },
          initialRoute: "/",
          onGenerateRoute: (settings) {
            if( settings.name == HYDetailScreen.routeName ) {
              return MaterialPageRoute(
                builder: (ctx) {
                  return HYDetailScreen(settings.arguments);
                }
              );
            }
            return null;
          },
          onUnknownRoute: (setting) {
    //        这个地方一般不会做很多的判断
            return MaterialPageRoute(
              builder: (ctx) {
                return HYErrorScreen();
              }
            );
          },
        );
      }
    }
    
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OYMDkjHJ-1586237096434)(8F12D5ABCF5340ADA277F1E18A79E733)]

    • 抽离代码

    但是我们这里代码 太过于集中 而且不好看

    MyApp里面东西太多了

    所以抽离代码

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bsmTlP07-1586237096435)(DCFCD86FF3714A66850E90D28DEEADA8)]

    逐渐把代码抽离过了来

    • alt + enter 点到对应的地方可以自动导包
    import '../about.dart';
    import '../main.dart';
    
    class HYRouter {
      static final Map<String, > routes ={
        HYHomePage.routeName: (ctx) => HYHomePage(),
        HYAboutPage.routeName: (ctx) => HYAboutPage(),
      };
    }
    

    这样我们就可以把所有要管理路由的东西放到router.dart里面管理了

    交到一个文件里面管理就有几个好处

    • 一个交过去以后MyApp里面代码变少了
    • 第二个我们要管理 直接到文件里改了 就不要每个东西 都在MyApp里面加了

    这个东西就是一个代码的封装

    import "package:flutter/material.dart";
    import 'package:learn_flutter02/day11_route_Protice/about.dart';
    import 'package:learn_flutter02/day11_route_Protice/detail.dart';
    import 'package:learn_flutter02/day11_route_Protice/error.dart';
    import 'package:learn_flutter02/day11_route_Protice/router/router.dart';
    
    main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Flutter Demo",
          theme: ThemeData(
            primarySwatch: Colors.blue, splashColor: Colors.transparent,
          ),
          routes: HYRouter.routes,
          initialRoute: HYRouter.initialRoute,
          onGenerateRoute: HYRouter.generateRoute,
          onUnknownRoute: HYRouter.unknownRoute,
        );
      }
    }
    
    class HYHomePage extends StatefulWidget {
      static final String routeName = "/";
    
      @override
      _HYHomePageState createState() => _HYHomePageState();
    }
    
    class _HYHomePageState extends State<HYHomePage> {
      String message  = "default";
    
      @override
      Widget build(BuildContext context) {
    
        return Scaffold(
          appBar: AppBar(
            title: Text("title"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                RaisedButton(
                  child: Text("跳转到详情页面"),
                  onPressed: () => _jumpToDetail(context),
                ),
                RaisedButton(
                  child: Text("跳转到关于页面页面"),
                  onPressed: () => _jumpToAbout(context),
                ),
                RaisedButton(
                  child: Text("跳转到详情页面"),
                  onPressed: () => _jumpToDetail2(context),
                ),
                RaisedButton(
                  child: Text("跳转到设置页面"),
                  onPressed: () => _jumpToSetting(context),
                ),
                Text(message, style: TextStyle(fontSize: 20))
              ],
            ),
          ),
        );
      }
    
      void _jumpToDetail(context) {
    //    1. 这个是要给普通的跳转方式
        Future res =  Navigator.of(context).push(
          MaterialPageRoute(
            builder: (BuildContext context) {
              return HYDetailScreen("a home message");
            }
          )
        );
        res.then((result) {
          print("首页获得的参数 $result");
          message = result;
        });
      }
    
      void _jumpToAbout(BuildContext context) {
        Future resFuture = Navigator.of(context).pushNamed(HYAboutPage.routeName, arguments: "a home message PushNamed");
        resFuture.then((res) {
          print("获得传递的参数值:$res");
        });
      }
    
      void _jumpToDetail2(BuildContext context) {
    //    按以前的方式 我们就这样
        Future resFuture = Navigator.of(context).pushNamed(HYDetailScreen.routeName, arguments: "a home Detail2 Message");
        resFuture.then((res) {
          print(res);
        });
      }
    
      void _jumpToSetting(BuildContext context) {
        Navigator.of(context).pushNamed("setting");
      }
    }
    

    router.dart

    import 'package:flutter/material.dart';
    
    import '../about.dart';
    import '../detail.dart';
    import '../error.dart';
    import '../main.dart';
    
    class HYRouter {
      static final Map<String, WidgetBuilder> routes ={
        HYHomePage.routeName: (ctx) => HYHomePage(),
        HYAboutPage.routeName: (ctx) => HYAboutPage(),
      };
    
      static final String initialRoute = "/";
    
      static final RouteFactory generateRoute = (settings) {
        if( settings.name == HYDetailScreen.routeName ) {
          return MaterialPageRoute(
              builder: (ctx) {
                return HYDetailScreen(settings.arguments);
              }
          );
        }
        return null;
      };
    
      static final RouteFactory unknownRoute = (setting) {
    //        这个地方一般不会做很多的判断
        return MaterialPageRoute(
            builder: (ctx) {
              return HYErrorScreen();
            }
        );
      };
    }
    

    文件结构

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yVCfCPni-1586237096435)(0CCA81A5AAAB473FB23C8E11A1EF2C53)]

    展开全文
  • flutter 手势识别GestureDetector

    千次阅读 2019-07-24 17:14:35
    GestureDetector是一个用于手势识别的功能性Widget,我们通过它可以来识别各种手势,它是指针事件的语义化封装 1.示例 import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; class...
  • Flutter开发App的过程中,我们除了需要灵活的使用各种组件之外,还需要掌握手势的识别,比如我们常常需要在操作App的时候使用到缩放,双击,放大,缩小等操作,这些Flutter都给我们提供了监听的组件...
  • Flutter 手势密码控件

    2019-07-21 18:09:04
    一个Flutter编写的手势识别验证锁。 例子 设置密码例子 GestureView( immediatelyClear: true, size: MediaQuery.of(context).size.width, onPanUp: (List<int> items) { setState(() { result = ...
  • Flutter 手势组件

    2021-11-16 14:22:41
    文章目录Flutter 手势组件...Flutter 手势组件 GestureDetector 点击相关 GestureDetector( onTapDown: (TapDownDetails details) { print("按下"); }, onTapUp: (TapUpDetails details) { print("抬起");
  • 本篇记录的是使用Flutter完成手势密码的功能,大致效果如下图所示: 该手势密码的功能比较简单,下面会详细记录实现的过程,另外还会简单说明如何将该手势密码作为插件发布到pub仓库。 开始 实现上面的手势密码并不...
  • 本篇文章来自诌在行的投稿,分享了flutter手势插件,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章。 诌在行的github地址: https://github.com/zhouzaihang / 前言 / 这个 Flutter Packge 是为了实现...
  • 手势操作 全局坐标与局部坐标 在GestureDetector中有两个重要属性值globalPosition和localPosition,两者都是Offset对象。 globalPosition就像它的命名表示当前手势触点在**全局坐标系位置与对应组件顶点坐标的偏移...
  • 1 signature 评分最高、支持web 2 flutter_signature_pad 支持横屏,手机和平板 3 hand_signature 支持svg和png格式导出
  • 在日常开发中,手势和事件无处不在,比如在 Flutter 应用中点击一个点赞按钮,长按弹出 BottomSheet 和商品列表的滑动等等都存在事件传递和手势识别,Flutter 内部是如何...
  • Flutter程序包可通过旋转和手势自定义提供360度的图像视图。 支持的Dart版本 Dart SDK版本> = 2.1.0 演示Gif 安装 添加包裹 dependencies : imageview360 : ^1.2.0 如何使用 将包导入到您的dart文件中 import '...
  • Flutter手势系统详解

    2018-08-08 11:28:26
    Flutter手势系统详解 我们知道,flutter 框架自动生成的入口方法为: void main() =&gt; runApp(new MyApp()); void runApp(Widget app) { WidgetsFlutterBinding.ensureInitialized() ..attachRootWidget...
  • import 'package:flutter/material.dart'; class GesturePage extends StatefulWidget { @override State<StatefulWidget> createState() { // TODO: implement createState return _GesturePageState();...
  • Flutter 手势GestureDetector解析

    千次阅读 2021-11-06 17:38:02
    对于移动端的开发者来说,...在Flutter中,对于Flutter有一定了解的人都知道,可以通过GestureDetector来给不具有点击事件或者手势回调的Widget添加手势回调。然后为了点击水波纹的点击效果,大多数开发者可能会使用I
  • flutter手势操作

    2013-03-03 09:52:49
    通过摄像头识别手势,操作音频视频播放软件,适用于windows自带播放器,以及itunes
  • 一、Flutter 点击事件处理、 二、GestureDetector 常用事件说明、 三、完整代码示例、 四、相关资源
  • 给wiget添加其它手势 onTap 点击 onDoubleTap 双击 onTapDown 按下去 onTapUp 松开 onTapCancel 取消,触发了 onTapDown 没触发 onTap onLongPress 长按 onVerticalFragStart 接触屏幕,可能垂直移动 ...
  • flutter 右滑手势

    2020-12-18 11:40:41
    import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; class RightBackDemo extends StatelessWidget { const RightBackDemo({Key key}) : super(key: key); @override Widget b...
  • App Store可以说是苹果业内设计的标杆了。 我们就来简单的实现一下 App Store的首页里其中的一...在Flutter中的手势事件分为两层。 第一层有原始指针事件,它描述了屏幕上指针(例如,触摸,鼠标和触控笔)的位置和...
  • import "package:flutter/material.dart"; import 'package:learn_flutter02/extension/size_fit.dart'; import "../extension/int_extension.dart"; main() => runApp(MyApp()); class...
  • Flutter手势监听

    2019-08-14 21:13:03
    常用的三个手势监听 OnTap:点击 OnDoubleTap:双击 OnLongPress:长按 import 'package:flutter/material.dart'; class Getsturepage extends StatefulWidget { @override _GetsturepageState createState...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,925
精华内容 1,170
关键字:

flutter手势