【Flutter从入门到实战】⑮、Flutter事件监听-以及路由使用、Flutter的指针事件Pointer、手势事件GestureDetector、event_bus事件总线、路由的概念、路由映射

本教程涵盖Flutter从环境搭建到实际项目开发全过程,包括Dart语言基础、Widget详解、事件监听及路由管理等内容,并通过豆瓣应用案例展示综合运用技巧。

Flutter从入门到实战
一共分为23个系列
①(Flutter、Dart环境搭建篇) 共3个内容 已更新
②(Dart语法1 篇) 共4个内容 已更新
③(Dart语法2 篇) 共2个内容 已更新
④(Flutter案例开发篇) 共4个内容 已更新
⑤(Flutter的StatelessWidget 共3个内容 已更新
⑥(Flutter的基础Widget篇) 共2个内容 已更新
⑦(布局Widget篇) 共1个内容 已更新
⑧(Flex、Row、Column以及Flexible、Stack篇) 共1个内容 已更新
⑨(滚动的Widget篇) 共4个内容 已更新
⑩(Dart的Future和网络篇) 共3个内容 已更新
⑪(豆瓣案例-1篇) 共3个内容 已更新
⑫(豆瓣案例-2篇) 共3个内容 已更新
⑬(Flutter渲染流程篇) 共3个内容 已更新
⑭(状态管理篇) 共3个内容 已更新
⑭(状态管理篇) 共3个内容 已更新
⑮(Flutter事件监听-以及路由使用篇) 共2个内容 已更新

官方文档说明

官方视频教程
Flutter的YouTube视频教程-小部件

请添加图片描述


⑮、Flutter事件监听篇

📚小贴士、FittedBox 做适配使用(比较实用)

官方文档

1.1 看一下我们之前写豆瓣首页的项目

比如星星的Widget。如果我把整体的Size调大。他会超出屏幕的范围。
我们如何修改?

  1. 调整字体的大小
  2. 使用FittedBox 他会进行一个整体的压缩
  3. 使用第三方库 进行一个比例系数 ,也可自己去实现一个rpx
    比如 我希望一个图片
    在iPhone5 是 90*90
    在iPhone6 是 100 * 100
    在iPhone6sPlus 是 110 *110

1.1 项目原始图

  Widget buildContentInfoRate(){
    return Row(
      children: [
        YHStarRating(rating: movie.rate,size: 40,),
        SizedBox(width: 6,),
        Text("${movie.rate}",style: TextStyle(fontSize:12)),

      ],
    );
  }

请添加图片描述

1.2 效果图 - 调整Size 超出屏幕范围

  Widget buildContentInfoRate(){
    return Row(
      children: [
        YHStarRating(rating: movie.rate,size: 300,),
        SizedBox(width: 6,),
        Text("${movie.rate}",style: TextStyle(fontSize:20)),

      ],
    );
  }

请添加图片描述

1.3 效果图 - 使用FittedBox 进行适配

  Widget buildContentInfoRate(){
    // FittedBox 是用来做适配的 整体做一个压缩
    return FittedBox(
      child: Row(
        children: [
          YHStarRating(rating: movie.rate,size: 300,),
          SizedBox(width: 6,),
          Text("${movie.rate}",style: TextStyle(fontSize:20)),

        ],
      ),
    );
  }

请添加图片描述


①、Flutter的事件监听

1、指针事件Pointer

PointerDownEvent
PointerMoveEvent
PointerUpEvent
PointerCancelEvent

1.1 指针事件Pointer的使用

使用指针事件 必须使用系统的 ListenerWidget包裹

1.2 代码实现 - 指针事件Pointer的使用
import 'package:flutter/material.dart'; // runApp在这个material库里面

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: YHiOSHomePage(),
    );
  }
}

class YHiOSHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("学习模板_Widget"),
      ),
      body: Listener(
        onPointerDown: (event){
          print("指针按下$event");
          print(event.position); // 获取指针在当前屏幕的位置
          print(event.localPosition); // 获取指针在当前Wdiget的位置

        },
        onPointerMove: (event){
          print("指针移动$event");
        },
        onPointerUp: (event){
          print("指针抬起$event");
        },
        child: Center(
          child: Container(
            width: 200,
            height: 200,
            color: Colors.red,
          ),
        ),
      ),
    );
  }
}

1.3 效果图 - 指针事件Pointer的使用

请添加图片描述

2、 手势事件 GestureDetector

1.手势

手势的监听有

  1. 按下
  2. 抬起
  3. 取消
  4. 点击
  5. 长按
  6. 双击
    等等
1.2 代码实现 - GestureDetector 的使用
class GestureDetectorDemoWidget extends StatelessWidget {
  const GestureDetectorDemoWidget({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTapDown: (details){
      print("手指按下");
      print(details.globalPosition);// 屏幕的所在位置
      print(details.localPosition); // 当前widget的所在位置
      },
      onTapUp: (details){
        print("手指抬起");
      },
      onTapCancel: (){
      print("手势取消");
      },
      onTap: (){
        print("手势点击");
      },
      onDoubleTap: (){
        print("手势双击");
      },
      onLongPress: (){
        print("手势长按");
      },

      child: Center(
          child: Container(
            width: 200,
            height: 200,
            color: Colors.pink,
      )
      ),
    );
  }
}
1.3、手势案例 - GestureDetector 叠加Widget问题 会出现内外穿透问题

使用Stack进行包裹
可以解决 GestureDetector 点击事件的穿透问题

1.3.1 不使用Stack 可能会引发点击事件的穿透 偶尔发送

可以使用
behavior: HitTestBehavior.opaque, // 解决外部穿透点击问题 但是可能还是会出现问题

1.3.1 代码 - 不使用Stack 可能会引发点击事件的穿透 偶尔发送
class YHiOSHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("手势模板_Widget"),
      ),
      body: Center(
        child: GestureDetector(
          onTapDown: (detils){
            print("outer click  内部点");
          },
          child: Container(
            width: 200,
            height: 200,
            color:  Colors.red,
            alignment: Alignment.center, //  包裹下一个Container 必须使用alignment
            child: GestureDetector(
              behavior: HitTestBehavior.opaque, // 解决外部穿透点击问题 但是可能还是会出现问题
              onTapDown: (detils){
                print("inner click  内部点");
              },
              child: Container(
                width: 100,
                height: 100,
                color:  Colors.blue,
              ),
            ),
          ),
        ),
      ),
    );
  }
}
1.3.2 使用Stack 包裹 GestureDetector 单独分离出来
1.3.2 代码 - 使用Stack 包裹 GestureDetector 单独分离出来
class GestureDetectorStackWidget extends StatelessWidget {
  const GestureDetectorStackWidget({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      //  使用Stack包裹手势 单独分离出来 这样就不会出现内外部点击穿透问题
      child:Stack(
        alignment: Alignment.center,
        children: [
          GestureDetector(
            onTapDown: (detils){
              print("outer click  内部点");
            },
            child: Container(
              width: 200,
              height: 200,
              color:  Colors.red,
              alignment: Alignment.center, //  包裹下一个Container 必须使用alignment
            ),
          ),
          GestureDetector(
            behavior: HitTestBehavior.opaque, // 解决外部穿透点击问题 但是可能还是会出现问题
            onTapDown: (detils){
              print("inner click  内部点");
            },
            child: Container(
              width: 100,
              height: 100,
              color:  Colors.blue,
            ),
          ),
        ],
      ),
    );
  }
}

3、Widget的数据传递 event_bus

可以考虑使用 事件总线 https://pub.dev/packages/event_bus

比如现在有个需求

  1. 有两个Widget 一个是Button 、一个是Text
  2. 点击按钮的时候 显示数据内容
3.1 event_bus使用模型数据传递
import 'package:flutter/material.dart'; // runApp在这个material库里面
import 'package:event_bus/event_bus.dart';

// 1. 创建全局的EventBus对象
final eventBus = EventBus();


class eventBusUserInfo {
  String nickname = "";
  int level = 0;

  eventBusUserInfo(this.nickname,this.level);
}

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: YHiOSHomePage(),
    );
  }
}

class YHiOSHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("手势模板_Widget"),
      ),
      body: EventBusDemoWidget(),
    );
  }
}

class EventBusDemoWidget extends StatelessWidget {
  const EventBusDemoWidget({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          YHButton(),
          YHText(),
        ],
      ),
    );
  }
}




class YHButton extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return RaisedButton(onPressed: (){
      // 2.发布事件
      // eventBus.fire("宇夜iOS");
      final info = eventBusUserInfo("宇夜iOS", 100);
      eventBus.fire(info);
    },
      child: Text("按钮"),
    );
  }
}

class YHText extends StatefulWidget {
  const YHText({Key? key}) : super(key: key);

  @override
  State<YHText> createState() => _YHTextState();
}

class _YHTextState extends State<YHText> {

  String _message = "Hello world";

  @override
  void initState() {
    super.initState();
    // 监听事件
    eventBus.on<eventBusUserInfo>().listen((data) {
      print(data.nickname);
      print(data.level);
      // 更新数据
      setState(() {
        _message  = "${data.nickname} - ${data.level}";
      });
    });
  }
  @override
  Widget build(BuildContext context) {
    return Text(_message,style: TextStyle(fontSize: 20),);
  }
}
3.1 效果图 - event_bus使用模型数据传递

请添加图片描述

  1. 创建全局的EventBus对象
    final eventBus = EventBus();
  2. 发布事件
    eventBus.fire(“宇夜iOS”);
  3. 监听事件
    eventBus.on().listen((data) {
    print(data.nickname);
    print(data.level);
    // 更新数据
    setState(() {
    _message = “${data.nickname} - ${data.level}”;
    });
    });

②、路由

还记得我们之前IndexStack我们做的豆瓣案例的页面切换
但是我们比如在首页 进入 首页详情页面。这种跳转来跳转去 一般是用Navigator来使用的 而Navigatorj就是路由
在Flutter中。路由管理主要有另两个类: Route和Navigator

1. Navigator

我们之前使用material风格。其实是包含Navigator的、
所以我们开发的Navigator不需要创建的

1.1 Navigator的push和pop 以及传递参数 以及反向回传参数

传递参数 需要在第二页面里面声明变量存储起来
返回传递参数

main.dart
import 'package:flutter/material.dart';
import 'package:learn_flutter/day11_%E4%BA%8B%E4%BB%B6/details.dart'; // runApp在这个material库里面

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: YHiOSHomePage(),
    );
  }
}

class YHiOSHomePage extends StatefulWidget {


  @override
  State<YHiOSHomePage> createState() => _YHiOSHomePageState();
}

class _YHiOSHomePageState extends State<YHiOSHomePage> {
   String _homeMessage = "";

  @override
  Widget build(BuildContext context) {
    // Navigator.of(context).push(route)

    return Scaffold(
      appBar: AppBar(
        title: Text("router模板_Widget"),
      ),
      body: Center(
        child:Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(_homeMessage,style: TextStyle(fontSize: 20),),
            RaisedButton(
              child: Text("跳转到详情"),
              onPressed: () => _jumpToDetails(context),
            ),
          ],
        ),
      ),
    );
  }

  void _jumpToDetails(BuildContext context){
    // Navigator.of(context).push(MaterialPageRoute(builder: (ctx) {
    //   // 1.普通跳转方式
    //   // 传递参数 : 通过构造器直接传递
    //   return YHDetailsScreent("a home message");
    //       }));
    // Navigator.push(context, route)
    // 2. 接受详情页面的数据
    Future result = Navigator.of(context).push(MaterialPageRoute(builder: (ctx) {
      // 1.正向传递参数
      return YHDetailsScreent("a home message");
    }));
    result.then((res) {
      print(res);
      setState(() {
        _homeMessage = res;
      });
    });
  }
}



1.2 反向传值方式1 重写导航的返回按钮

重写导航的返回按钮
appBar: AppBar(
title: Text(“详情页”),
// 自定义导航栏的返回按钮 并且监听返回的事件处理 回传参数
leading: IconButton(
icon: Icon(Icons.backspace),
onPressed: () => _backToHome(context),
),
)

import 'package:flutter/material.dart';

// 详情页面
class YHDetailsScreent extends StatelessWidget {
  final String _message;
  // const YHDetailsScreent({Key? key}) : super(key: key);
  YHDetailsScreent(this._message);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("详情页"),
        // 自定义导航栏的返回按钮 并且监听返回的事件处理 回传参数
        leading: IconButton(
          icon: Icon(Icons.backspace),
          onPressed: () => _backToHome(context),
        ),
      ),
      body: Center(
        child: Column(
          children: [
            Text(_message,style: TextStyle(fontSize: 20),),
            RaisedButton(child: Text("回到首页"), onPressed: () => _backToHome(context)
            ),
          ],
        ),
      ),
    );
  }

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

}

1.2 反向传值方式2 WillPopScope
import 'package:flutter/material.dart';

// 详情页面
class YHDetailsScreent extends StatelessWidget {
  final String _message;
  // const YHDetailsScreent({Key? key}) : super(key: key);
  YHDetailsScreent(this._message);
  @override
  Widget build(BuildContext context) {
    // 反向传值 方式2 使用 WillPopScope
    return WillPopScope(
      onWillPop: (){
        print("反向传值 方式2 使用 WillPopScope");
        _backToHome(context);
        return Future.value(false);
      },

      child: Scaffold(
        appBar: AppBar(
          title: Text("详情页"),
          // 反向传值 方式1 重写导航返回按钮的事件
          // 自定义导航栏的返回按钮 并且监听返回的事件处理 回传参数
          // leading: IconButton(
          //   icon: Icon(Icons.backspace),
          //   onPressed: () => _backToHome(context),
          // ),
        ),
        body: Center(
          child: Column(
            children: [
              Text(_message,style: TextStyle(fontSize: 20),),
              RaisedButton(child: Text("回到首页"), onPressed: () => _backToHome(context)
              ),
            ],
          ),
        ),
      ),
    );
  }

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

}

1.2 效果图 - 反向传值方式2 WillPopScope

请添加图片描述

2. 路由映射

如果开发过程中,我们跳转的详情代码 很长。
此时 我们可以使用路由映射。
也就是相当于给跳转页面 起别名的意思。

  1. 给页面命名
  2. 在MyApp设置路由
2.1 添加多一个关于页面作为演示路由映射
main.dart
import 'package:flutter/material.dart';
import 'package:learn_flutter/day11_%E4%BA%8B%E4%BB%B6/about.dart';
import 'package:learn_flutter/day11_%E4%BA%8B%E4%BB%B6/details.dart'; // runApp在这个material库里面

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // home: YHiOSHomePage(), // 如果有 路由并且设置initialRoute 那么home不用设置
      routes: {
        "/": (cxt) => YHiOSHomePage(),
        "/about": (ctx) => YHAboutScreent(),
      },
      initialRoute: "/", // 初始化路由的页面为首页
    );
  }
}

class YHiOSHomePage extends StatefulWidget {


  @override
  State<YHiOSHomePage> createState() => _YHiOSHomePageState();
}

class _YHiOSHomePageState extends State<YHiOSHomePage> {
   String _homeMessage = "";

  @override
  Widget build(BuildContext context) {
    // Navigator.of(context).push(route)

    return Scaffold(
      appBar: AppBar(
        title: Text("router模板_Widget"),
      ),
      body: Center(
        child:Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(_homeMessage,style: TextStyle(fontSize: 20),),
            RaisedButton(
              child: Text("跳转到详情"),
              onPressed: () => _jumpToDetails(context),
            ),
            RaisedButton(
              child: Text("跳转到关于页"),
              onPressed: () => _jumpToAbouts(context),
            ),
          ],
        ),
      ),
    );
  }

  void _jumpToDetails(BuildContext context){
    // Navigator.of(context).push(MaterialPageRoute(builder: (ctx) {
    //   // 1.普通跳转方式
    //   // 传递参数 : 通过构造器直接传递
    //   return YHDetailsScreent("a home message");
    //       }));
    // Navigator.push(context, route)
    // 2. 接受详情页面的数据
    Future result = Navigator.of(context).push(MaterialPageRoute(builder: (ctx) {
      // 1.正向传递参数
      return YHDetailsScreent("a home message");
    }));
    result.then((res) {
      print(res);
      setState(() {
        _homeMessage = res;
      });
    });
  }


   void _jumpToAbouts(BuildContext context){
    // 跳转关于页
    Navigator.of(context).pushNamed("/about");
   }
}




about.dart
import 'package:flutter/material.dart';

class YHAboutScreent extends StatelessWidget {
  const YHAboutScreent({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("关于页"),
      ),
      body: Column(
        children: [
          Text("11111"),
          RaisedButton(
            child: Text("返回首页"),
              onPressed: (){

          }),
        ],
      ),
    );
  }
}

2.1 效果图 - 添加多一个关于页面作为演示路由映射

请添加图片描述

2.3 ⭐️将路由映射的名字定义成常量、 将路由映射进行封装
  1. 定义成常量的好好处是 防止写错
    规范: 定义在每个页面里面

  2. 封装的好处: 利于开发管理

2.3.1 路由映射的名字定义成常量 - 以跳转到关于页面为例 并且获取参数
  1. 在About页面 声明变量
    static const String routeName = “/about”;
  2. 在MyApp的Route里面配置路由映射的名字
    routes: {
    YHiOSHomePage.routeName: (cxt) => YHiOSHomePage(),
    YHAboutScreent.routeName: (ctx) => YHAboutScreent(),
    },
    initialRoute: YHiOSHomePage.routeName, // 初始化路由的页面为首页
  3. 页面通过pushNamed跳转时 并且传递参数。并且获取关于页面返回传递的参数
final result =  Navigator.of(context).pushNamed(YHAboutScreent.routeName,arguments: > "a > home message");
  // 关于页面 返回获取数据
  result.then((value) {
    print("about page pop value is ${value}");
  });
  1. 获取传递过来的参数
    final String message = ModalRoute.of(context)?.settings.arguments as String;

  2. 返回上一个页面并且传递参数
    Navigator.of(context).pop("123");

2.3.2 命名路由跳转方式2- 钩子函数

不重构的情况下
不使用构造函数的方式进行跳转

2.3.2 命名路由跳转方式2- 钩子函数 - 以跳转详情页面为例子

我们在上面写了一个跳转详情页面的时候 使用的是构造函数进行跳转。
也就是说。我们每次跳转都需要传递一个message
Future result = Navigator.of(context).push(MaterialPageRoute(builder: (ctx) {
// 1.正向传递参数
return YHDetailsScreent(“a home message”);
}));
比如 我现在的需求 是不传递行不行

  1. 可以使用钩子函数
  2. 在MyApp里面配置 onGenerateRoute
  3. onGenerateRoute里面判断是哪个路由映射的名字 就处理创建哪个页面
  4. onUnknownRoute 配置错误页面
main.dart
import 'package:flutter/material.dart';
import 'package:learn_flutter/day11_%E4%BA%8B%E4%BB%B6/about.dart';
import 'package:learn_flutter/day11_%E4%BA%8B%E4%BB%B6/details.dart'; // runApp在这个material库里面
import 'package:learn_flutter/day11_%E4%BA%8B%E4%BB%B6/unknown.dart'; // runApp在这个material库里面

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // home: YHiOSHomePage(), // 如果有 路由并且设置initialRoute 那么home不用设置
      routes: {
        YHiOSHomePage.routeName: (cxt) => YHiOSHomePage(),
        YHAboutScreent.routeName: (ctx) => YHAboutScreent(),
      },
      // 钩子函数 - 能监听路由的名字
      onGenerateRoute: (settings) {
        if (settings.name == YHDetailsScreent.routeName) {
          return MaterialPageRoute(
              builder: (ctx) {
                // 钩子函数 获取传递的参数
                final String args = settings.arguments as String;
                return YHDetailsScreent(args);
              }
          );
        }
        return null;
      },
      onUnknownRoute: (settings){
        return MaterialPageRoute(builder: (ctx){
          return YHUnknownScreent();
        });
      },
      initialRoute: YHiOSHomePage.routeName, // 初始化路由的页面为首页
    );
  }
}

class YHiOSHomePage extends StatefulWidget {
  static final String routeName = "/";

  @override
  State<YHiOSHomePage> createState() => _YHiOSHomePageState();
}

class _YHiOSHomePageState extends State<YHiOSHomePage> {
   String _homeMessage = "";

  @override
  Widget build(BuildContext context) {
    // Navigator.of(context).push(route)

    return Scaffold(
      appBar: AppBar(
        title: Text("router模板_Widget"),
      ),
      body: Center(
        child:Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(_homeMessage,style: TextStyle(fontSize: 20),),
            RaisedButton(
              child: Text("跳转到详情"),
              onPressed: () => _jumpToDetails(context),
            ),
            RaisedButton(
              child: Text("跳转到关于页"),
              onPressed: () => _jumpToAbouts(context),
            ),
            RaisedButton(
              child: Text("跳转到详情2"),
              onPressed: () => _jumpToDetails2(context),
            ),
            RaisedButton(
              child: Text("跳转到设置"),
              onPressed: () => _jumpToSettings(context),
            ),
          ],
        ),
      ),
    );
  }

  void _jumpToDetails(BuildContext context){
    // Navigator.of(context).push(MaterialPageRoute(builder: (ctx) {
    //   // 1.普通跳转方式
    //   // 传递参数 : 通过构造器直接传递
    //   return YHDetailsScreent("a home message");
    //       }));
    // Navigator.push(context, route)
    // 2. 接受详情页面的数据
    Future result = Navigator.of(context).push(MaterialPageRoute(builder: (ctx) {
      // 1.正向传递参数
      return YHDetailsScreent("a home message");
    }));
    result.then((res) {
      print(res);
      setState(() {
        _homeMessage = res;
      });
    });
  }


   void _jumpToAbouts(BuildContext context){
    // 跳转关于页
     // pushNamed 传递参数 使用arguments
    final result =  Navigator.of(context).pushNamed(YHAboutScreent.routeName,arguments: "a home message");
    // 关于页面 返回获取数据
    result.then((value) {
      print("about page pop value is ${value}");
    });
  }


   void _jumpToDetails2(BuildContext context) {

     final result = Navigator.of(context).pushNamed(YHDetailsScreent.routeName,arguments: "a details2 message");
     result.then((value) {
       print("details2 page pop value is ${value}");
     });
   }

   void _jumpToSettings(BuildContext context) {

     final result = Navigator.of(context).pushNamed("/settings");

   }
}




details.dart
import 'package:flutter/material.dart';

// 详情页面
class YHDetailsScreent extends StatelessWidget {
  static const  String  routeName = "/details";

  final String _message;
  // const YHDetailsScreent({Key? key}) : super(key: key);
  YHDetailsScreent(this._message);
  @override
  Widget build(BuildContext context) {
    // 反向传值 方式2 使用 WillPopScope

    return WillPopScope(
      onWillPop: (){
        print("反向传值 方式2 使用 WillPopScope");
        _backToHome(context);
        return Future.value(false);
      },

      child: Scaffold(
        appBar: AppBar(
          title: Text("详情页"),
          // 反向传值 方式1 重写导航返回按钮的事件
          // 自定义导航栏的返回按钮 并且监听返回的事件处理 回传参数
          // leading: IconButton(
          //   icon: Icon(Icons.backspace),
          //   onPressed: () => _backToHome(context),
          // ),
        ),
        body: Center(
          child: Column(
            children: [
              Text(_message,style: TextStyle(fontSize: 20),),
              RaisedButton(child: Text("回到首页"), onPressed: () => _backToHome(context)
              ),
            ],
          ),
        ),
      ),
    );
  }

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

}

unknown.dart
import 'package:flutter/material.dart';

class YHUnknownScreent extends StatelessWidget {
  const YHUnknownScreent({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("错误页面"),
      ),
      body: Center(
        child: Text("错误页面",style: TextStyle(fontSize: 20,color: Colors.red),),
      ),
    );
  }
}

2.3.2 效果图 - 命名路由跳转方式2- 钩子函数 - 以跳转详情页面为例子

请添加图片描述

2.3.3 将路由映射进行封装

从上面的代码看出 钩子函数 一个页面的处理很多
在开发过程中 我们一般情况是进行路由映射进行封装
方便管理

YHRoute.dart - 路由映射封装
import 'package:flutter/material.dart';
import 'package:learn_flutter/day09%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/03_globalKey%E7%9A%84%E4%BD%BF%E7%94%A8.dart';
import '../main.dart';
import '../about.dart';
import '../details.dart';
import '../unknown.dart';


class YHRoute {
  // 路由映射
  static final Map<String,WidgetBuilder> routes = {
  YHiOSHomePage.routeName: (cxt) => YHiOSHomePage(),
  YHAboutScreent.routeName: (ctx) => YHAboutScreent(),
  };

  // 首页初始化路由
  static final String initialRoute = YHiOSHomePage.routeName;

  // 钩子函数
  static final RouteFactory generateRoute = (settings) {
    if (settings.name == YHDetailsScreent.routeName) {
      return MaterialPageRoute(
          builder: (ctx) {
            // 钩子函数 获取传递的参数
            final String args = settings.arguments as String;
            return YHDetailsScreent(args);
          }
      );
    }
    return null;
  };

  static final RouteFactory unknownRoute = (settings){
    return MaterialPageRoute(builder: (ctx){
    return YHUnknownScreent();
    });
  };

}

main.dart

路由封装的使用

main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // home: YHiOSHomePage(), // 如果有 路由并且设置initialRoute 那么home不用设置
      routes: YHRoute.routes,
      // 钩子函数 - 能监听路由的名字
      onGenerateRoute: YHRoute.generateRoute,
      onUnknownRoute: YHRoute.unknownRoute,
      initialRoute: YHRoute.initialRoute, // 初始化路由的页面为首页
    );
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宇夜iOS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值