Flutter入门09 -- 事件监听与路由跳转

  • 在大前端的开发中,必然存在各种各样与用户交互的情况,比如手指点击,滑动,长按等等;
  • 在Flutter中,手势有两个不同的层次:
    • 第一层:原始指针事件,描述了屏幕上由触摸板,鼠标,指示笔等触发的指针的位置和移动;
    • 第二层:手势识别,这个是在原始事件上的一种封装;

指针事件Pointer

  • Pointer表示人机界面的原始数据,一共有四种指针事件:
    • PointerDownEvent:指针在特定位置与屏幕接触;
    • PointerMoveEnent:指针从屏幕的一个位置移动到另一个位置;
    • PointerUpEvent:指针与屏幕停止接触;
    • PointerCancelEvent:指针因为某些特殊的情况被取消;
  • Pointer事件的原理:在指针落下时,框架做了一个Hit Test操作,确定与屏幕发生接触的位置上有哪些widget以及分发给最内部的组件去响应;事件会沿着最内部的组件向组件树的根冒泡分发;不能取消或者停止事件的进一步分发;
  • 基本上与iOS的事件传递与响应机制的原理一致;
  • 案例代码:
import 'package:flutter/material.dart';

main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage(),
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("商品列表"),
      ),
      body: Center(
        child: Listener(
          onPointerDown: (event) {
            print("手指按下: $event");
            print(event.position);
            print(event.localPosition);
          },
          onPointerMove: (event) {
            print("手指移动: $event");
          },
          onPointerUp: (event) {
            print("手指抬起: $event");
          },
          onPointerCancel: (event) {
            print("取消 $event");
          },
          child: Container(
            width: 100,
            height: 100,
            color: Colors.red,
          ),
        ),
      )
    );
  }
}
  • 给目标组件Container包装一个事件监听Listener,内部实现各种不同事件的回调方法;
  • event.position:是以屏幕左上角为原点;
  • event.localPosition:是以目标组件Container的左上角为原点;

手势识别

  • 手势是对一系列Pointer的封装,官方建议在开发中尽可能使用手势,而不是Pointer;
手势的分类:
  • 点击
    • OnTapDown:手指按下
    • OnTapUp:手指抬起
    • OnTap:点击事件完成
    • OnTapCancel:事件取消
  • 双击
    • OnDoubleTap:快速点击了两次
  • 长按
    • OnLongPress:在屏幕上保持了一段时间
  • 纵向拖拽
    • OnVerticalDragStart:指针与屏幕产生接触并可能开始纵向移动;
    • OnVerticalDragUpdate:指针与屏幕产生接触,在纵向上发生移动并保持移动;
    • OnVerticalDragEnd:指针与屏幕产生接触结束;
  • 横向拖拽
    • OnHorizontalDragStart:指针与屏幕产生接触并可能开始横向移动;
    • OnHorizontalDragUpdate:指针与屏幕产生接触,在横向上发生移动并保持移动;
    • OnHorizontalDragEnd:指针与屏幕产生接触结束;
  • 移动
    • OnPathStart:指针与屏幕产生接触并可能开始横向移动或者纵向移动,如果设置了onHorizontalDragStart或者onVerticalDragStart,该回调会引发崩溃
    • OnPathUpdate:指针与屏幕产生接触,在横向或者纵向上发生移动并保持移动,如果设置了OnHorizontalDragUpdate或者OnVerticalDragUpdate,该回调会引发崩溃
    • OnPathEnd:指针与屏幕产生了接触,并以特定的速度移动,此后不再在屏幕上接触发生移动,如果设置了OnHorizontalDragEnd或者OnVerticalDragEnd,该回调会引发崩溃
  • 案例演练:
import 'package:flutter/material.dart';

main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage(),
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("商品列表"),
        ),
        body: Center(
          child: GestureDetector(
            onTapDown: (details) {
              print("手指按下");
              print(details.globalPosition);
              print(details.localPosition);
            },
            onTapUp: (details) {
              print("手指抬起");
            },
            onTapCancel: () {
              print("手势取消");
            },
            onTap: () {
              print("点击");
            },

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

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

            child: Container(
              width: 200,
              height: 200,
              color: Colors.greenAccent,
            ),
          ),
        )
    );
  }
}
  • 给目标组件Container,包装一个GestureDetector手势管理器,实现各种手势的监听回调函数;
案例演练:组件重叠显示时的手势识别
import 'package:flutter/material.dart';

main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage(),
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("商品列表"),
      ),
      body: Center(
        child: GestureDetector(
          onTapDown: (details) {
            print("outside click");
          },
          child: Container(
            width: 200,
            height: 200,
            color: Colors.red,
            alignment: Alignment.center,
            child: GestureDetector(
              onTapDown: (details) {
                print("inside click");
              },
              child: Container(
                width: 100,
                height: 100,
                color: Colors.green,
              ),
            ),
          ),
        ),
      )
    );
  }
}
  • 运行效果如下所示:
image.png
  • 两个组件都加了手势监听,当点击绿色组件的时候,红色组件偶尔也会出现监听回调,为了解决这个问题,改变组件的布局层级,利用Stack组件,代码如下:
import 'package:flutter/material.dart';

main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage(),
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("商品列表"),
      ),
      body: Center(
        child: Stack(
          alignment: Alignment.center,
          children: [
            GestureDetector(
              onTapDown: (details) {
                print("red click");
              },
              child: Container(
                width: 200,
                height: 200,
                color: Colors.red,
                alignment: Alignment.center,
              )
            ),

            GestureDetector(
              onTapDown: (details) {
                print("greenAccent click");
              },
              child: Container(
                width: 100,
                height: 100,
                color: Colors.green,
              ),
            )
          ],
        )
      )
    );
  }
}
  • 当点击绿色组件的时候,只有绿色组件响应手势,红色组件不会响应;

  • 现在想要实现:当点击绿色组件时,只有红色组件响应手势事件,即实现了事件的穿透效果,代码如下:

import 'package:flutter/material.dart';

main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage(),
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("商品列表"),
      ),
      body: Center(
        child: Stack(
          alignment: Alignment.center,
          children: [
            GestureDetector(
              onTapDown: (details) {
                print("red click");
              },
              child: Container(
                width: 200,
                height: 200,
                color: Colors.red,
                alignment: Alignment.center,
              )
            ),

            IgnorePointer(
              child: GestureDetector(
                onTapDown: (details) {
                  print("greenAccent click");
                },
                child: Container(
                  width: 100,
                  height: 100,
                  color: Colors.green,
                ),
              ),
            )
          ],
        )
      )
    );
  }
}
  • 给绿色组件 最外层包裹了一个IgnorePointer,实现了手势事件的忽略;
跨组件事件的传递
  • 使用第三方库event_bus实现跨组件事件的传递;
  • 工程中引用event_bus
dependencies:
  event_bus: ^1.1.1
  • 使用步骤:
    • 1.创建全局的EventBus对象;

    • 2.发出事件;

      1. 监听事件的类型;
  • 案例代码如下:
import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';

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

class SFUserInfo {
  String name;
  int level;

  SFUserInfo(this.name,this.level);
}

main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage(),
    );
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("商品列表"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            SFButton(),
            SFText(),
          ],
        ),
      )
    );
  }
}

class SFButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text("按钮"),
      onPressed: () {
        print("点击按钮");
        //2.发出事件
        final userInfo = SFUserInfo("liyanyan",100);
        eventBus.fire(userInfo);
      },
    );
  }
}

class SFText extends StatefulWidget {
  @override
  _SFTextState createState() => _SFTextState();
}

class _SFTextState extends State<SFText> {

  String _message = "Hello World";

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    //3.监听事件的类型
    eventBus.on<SFUserInfo>().listen((data) {
      print(data.name);
      print(data.level);
      _message = "${data.name} -- ${data.level}";
    });
  }

  @override
  Widget build(BuildContext context) {
    return Text("Hello World!!!",style: TextStyle(fontSize: 25));
  }
}

路由管理

  • 路由的概念由来已久,包括网络路由后端路由,到现在广为流行的前端路由
  • 无论路由的概念如何应用,它的核心是一个路由映射表,例如名字detail映射到DetailPage页面;
  • 有了映射表之后,我们就可以方便的根据名字来完成路由的转发,即页面跳转;
  • 在Flutter中,路由管理主要有两个类RouteNavigator
Route
  • Route:一个页面要想被路由统一管理,必须包装为一个Route,Route是一个抽象类,所以只能实例化其子类;
  • MaterialPageRoute是使用最广泛的路由,其在不同的平台会有不同的表现:
    • 在Android平台,打开一个页面从屏幕底部滑到屏幕顶部;
    • 在iOS平台,打开一个页面从屏幕右侧滑到屏幕左侧;
    • 在iOS平台还可以使用CupertinoPageRoute
Navigator
  • Navigator:管理所有的Route的widget,通过一个栈进行管理的;
  • 在开发中我们不需要手动去创建一个Navigator,因为在开发中使用的MaterialApp,CupertinoApp,WidgetsApp它们默认是有插入Navigator的,所以,我们在需要的时候,只需要直接使用即可;
路由的基本使用
  • 需求:首页与详情页两个页面,当首页->详情页 携带数据过去,当详情页 -> 首页 传递数据给首页,代码如下:
  • 首页代码:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:learn_flutter_01/day02/SFDetail.dart';

main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SFHomePage(),
    );
  }
}

class SFHomePage extends StatefulWidget {
  @override
  _SFHomePageState createState() => _SFHomePageState();
}

class _SFHomePageState extends State<SFHomePage> {

  String homeMessage = "default message";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("首页"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(homeMessage,style: TextStyle(fontSize: 20)),
            RaisedButton(
              child: Text("跳转到详情页面"),
              onPressed: () {
                goToDetail(context);
              },
            )
          ],
        )
      )
    );
  }

  void goToDetail(BuildContext ctx) {
    Future result = Navigator.of(ctx).push(MaterialPageRoute(
      builder: (ctx) {
        return SFDetail("liyanyan");
      }
    ));

    result.then((value) {
      print(value);
      setState(() {
        homeMessage = value;
      });
    });
  }
}
  • 详情页代码:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class SFDetail extends StatelessWidget {
  final String message;

  SFDetail(this.message);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("详情页"),
        leading: IconButton(
          icon: Icon(Icons.backspace),
          onPressed: () => backHomePage(context),
        ),
      ),
      body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(message,style: TextStyle(fontSize: 20)),
              RaisedButton(
                child: Text("返回首页"),
                onPressed: () => backHomePage(context),
              )
            ],
          )
      ),
    );
  }

  void backHomePage(BuildContext ctx) {
    Navigator.of(ctx).pop("xie shen fang");
  }
}
  • 首页 -> 详情页 创建MaterialPageRoute路由
void goToDetail(BuildContext ctx) {
    Future result = Navigator.of(ctx).push(MaterialPageRoute(
      builder: (ctx) {
        return SFDetail("liyanyan");
      }
    ));

    result.then((value) {
      print(value);
      setState(() {
        homeMessage = value;
      });
    });
  }
  • 详情页 -> 首页
void backHomePage(BuildContext ctx) {
    Navigator.of(ctx).pop("xie shen fang");
  }
  • 在详情页 去除了系统提供的返回按钮,使用自己的按钮;
  • 在详情页,使用系统的提供返回的按钮,但是我们要重写点击事件的逻辑,可以使用WillPopScope,如下所示:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class SFDetail extends StatelessWidget {
  final String message;

  SFDetail(this.message);

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () {
        //当返回为true时,可以自动返回;
        //当返回为false时,不能自动返回首页,需要我们手写代码进行返回;
        backHomePage(context);
        return Future.value(false);
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text("详情页"),
        ),
        body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(message,style: TextStyle(fontSize: 20)),
                RaisedButton(
                  child: Text("返回首页"),
                  onPressed: () => backHomePage(context),
                )
              ],
            )
        ),
      ),
    );
  }

  void backHomePage(BuildContext ctx) {
    Navigator.of(ctx).pop("xie shen fang");
  }
}
  • onWillPop参数,传入函数,其函数的返回值为Future.value(bool)

  • 当bool = true时,可以自动返回;

  • 当bool = false时,不能自动返回首页,需要我们手写代码进行返回;

  • 上述采用的简单的路由跳转,下面介绍路由表映射的跳转,再新建一个SFAbout页面:

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

class SFAbout extends StatelessWidget {

  static const String routeName = "SFAbout";

  @override
  Widget build(BuildContext context) {

    final message = ModalRoute.of(context).settings.arguments as String;
    
    return Scaffold(
      appBar: AppBar(
        title: Text("关于页面"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(message),
            RaisedButton(
              child: Text("返回首页"),
              onPressed: () {

              },
            )
          ],
        ),
      ),
    );
  }
}
  • 首页代码改造如下:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:learn_flutter_01/day02/SFAbout.dart';
import 'package:learn_flutter_01/day02/SFDetail.dart';

main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      routes: {
        SFHomePage.routeName : (ctx) => SFHomePage(),
        SFAbout.routeName : (ctx) => SFAbout()
      },
      initialRoute: SFHomePage.routeName
    );
  }
}

class SFHomePage extends StatefulWidget {

  static const String routeName = "/";

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

class _SFHomePageState extends State<SFHomePage> {

  String homeMessage = "default message";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("首页"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(homeMessage,style: TextStyle(fontSize: 20)),
            RaisedButton(
              child: Text("跳转到详情页面"),
              onPressed: () {
                goToDetail(context);
              },
            ),
            RaisedButton(
              child: Text("跳转到关于页面"),
              onPressed: () {
                goToAbout(context);
              },
            )
          ],
        )
      )
    );
  }

  void goToDetail(BuildContext ctx) {
    Future result = Navigator.of(ctx).push(MaterialPageRoute(
      builder: (ctx) {
        return SFDetail("liyanyan");
      }
    ));

    result.then((value) {
      print(value);
      setState(() {
        homeMessage = value;
      });
    });
  }

  void goToAbout(BuildContext ctx) {
    Future result = Navigator.of(context).pushNamed(SFAbout.routeName,arguments: "liyanyan");

    result.then((value) {
      print(value);
    });
  }
}
  • MaterialApp组件中,传入routesinitialRoute两个参数,其中routes表示路由表,initialRoute表示App的首页路由为SFHomePage();
  • 在首页 跳转 SFAbout页面时,执行:Navigator.of(context).pushNamed(SFAbout.routeName,arguments: "liyanyan") 其中arguments为携带的数据;
  • 在SFAbout页面接收到首页的数据:final message = ModalRoute.of(context).settings.arguments as String
  • SFDetail页面的构造函数,定义了一个入参,那么在MaterialApproutes,写路由映射时,不能传参的,这时就引入了onGenerateRoute,再次生成一个新的路由,可传参的,实现如下:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:learn_flutter_01/day02/SFAbout.dart';
import 'package:learn_flutter_01/day02/SFDetail.dart';

main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      routes: {
        SFHomePage.routeName : (ctx) => SFHomePage(),
        SFAbout.routeName : (ctx) => SFAbout()
      },
      initialRoute: SFHomePage.routeName,
      onGenerateRoute: (setttings) {
        if (setttings.name == SFDetail.routeName) {
          return MaterialPageRoute(
              builder: (ctx) {
                return SFDetail(setttings.arguments);
              }
          );
        }
        return null;
      }
    );
  }
}

class SFHomePage extends StatefulWidget {

  static const String routeName = "/";

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

class _SFHomePageState extends State<SFHomePage> {

  String homeMessage = "default message";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("首页"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(homeMessage,style: TextStyle(fontSize: 20)),
            RaisedButton(
              child: Text("跳转到详情页面"),
              onPressed: () {
                goToDetail(context);
              },
            ),
            RaisedButton(
              child: Text("跳转到关于页面"),
              onPressed: () {
                goToAbout(context);
              },
            ),
            RaisedButton(
              child: Text("跳转到详情页面"),
              onPressed: () {
                goToDetail2(context);
              },
            )
          ],
        )
      )
    );
  }

  void goToDetail(BuildContext ctx) {
    Future result = Navigator.of(ctx).push(MaterialPageRoute(
      builder: (ctx) {
        return SFDetail("liyanyan");
      }
    ));

    result.then((value) {
      print(value);
      setState(() {
        homeMessage = value;
      });
    });
  }

  void goToAbout(BuildContext ctx) {
    Future result = Navigator.of(context).pushNamed(SFAbout.routeName,arguments: "liyanyan");

    result.then((value) {
      print(value);
    });
  }

  void goToDetail2(BuildContext ctx) {
    Navigator.of(context).pushNamed(SFDetail.routeName,arguments: "123");
  }
}
  • 如果跳转的页面 在路由映射表中 没有注册,就会跳转报错,一般我们定义一个错误页面SFUnKnownPage,代码如下:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class SFUnKnownPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("错误页面"),
      ),
      body: Center(
        child: Text("错误页面"),
      ),
    );
  }
}
  • 然后在MaterialApp中,新增参数onUnknownRoute,传入错误页面的路由,如下所示:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:learn_flutter_01/day02/SFAbout.dart';
import 'package:learn_flutter_01/day02/SFDetail.dart';
import 'package:learn_flutter_01/day02/SFUnknownPage.dart';

main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      routes: {
        SFHomePage.routeName : (ctx) => SFHomePage(),
        SFAbout.routeName : (ctx) => SFAbout()
      },
      initialRoute: SFHomePage.routeName,
      onGenerateRoute: (setttings) {
        if (setttings.name == SFDetail.routeName) {
          return MaterialPageRoute(
              builder: (ctx) {
                return SFDetail(setttings.arguments);
              }
          );
        }
        return null;
      },
      onUnknownRoute: (settings) {
        return MaterialPageRoute(
          builder: (ctx) {
            return SFUnKnownPage();
          }
        );
      },
    );
  }
}

class SFHomePage extends StatefulWidget {

  static const String routeName = "/";

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

class _SFHomePageState extends State<SFHomePage> {

  String homeMessage = "default message";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("首页"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(homeMessage,style: TextStyle(fontSize: 20)),
            RaisedButton(
              child: Text("跳转到详情页面"),
              onPressed: () {
                goToDetail(context);
              },
            ),
            RaisedButton(
              child: Text("跳转到关于页面"),
              onPressed: () {
                goToAbout(context);
              },
            ),
            RaisedButton(
              child: Text("跳转到详情页面"),
              onPressed: () {
                goToDetail2(context);
              },
            ),
            RaisedButton(
              child: Text("跳转到设置页面"),
              onPressed: () {
                goToAbou2(context);
              },
            )
          ],
        )
      )
    );
  }

  void goToDetail(BuildContext ctx) {
    Future result = Navigator.of(ctx).push(MaterialPageRoute(
      builder: (ctx) {
        return SFDetail("liyanyan");
      }
    ));

    result.then((value) {
      print(value);
      setState(() {
        homeMessage = value;
      });
    });
  }

  void goToAbout(BuildContext ctx) {
    Future result = Navigator.of(context).pushNamed(SFAbout.routeName,arguments: "liyanyan");

    result.then((value) {
      print(value);
    });
  }

  void goToDetail2(BuildContext ctx) {
    Navigator.of(context).pushNamed(SFDetail.routeName,arguments: "123");
  }

  void goToAbou2(BuildContext ctx) {
    ///setting为定义的页面 跳转页面会直接报错
    Navigator.of(context).pushNamed("/settings");
  }
}
  • setting为定义的页面 跳转页面会直接报错;

  • 代码封装,将有关的路由封装到一个专门处理路由的类SFRouter,如下所示:


import 'package:flutter/material.dart';
import 'package:learn_flutter_01/day02/SFAbout.dart';
import 'package:learn_flutter_01/day02/SFDetail.dart';
import 'package:learn_flutter_01/main.dart';
import '../SFUnknownPage.dart';

class SFRouter {

  static final Map<String,WidgetBuilder> routers = {
    SFHomePage.routeName : (ctx) => SFHomePage(),
    SFAbout.routeName : (ctx) => SFAbout(),
  };

  static final String initialRoute = SFHomePage.routeName;

  static final RouteFactory generateRoute = (settings) {
    if (settings.name == SFDetail.routeName) {
      return MaterialPageRoute(
        builder: (ctx) {
          return SFDetail(settings.arguments);
        }
      );
    }
    return null;
  };

  static final RouteFactory unknownRoute = (settings) {
    return MaterialPageRoute(
        builder: (ctx) {
          return SFUnKnownPage();
        }
    );
  };
}
  • 首页调用路由就变得十分简洁了,如下所示:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:learn_flutter_01/day02/SFAbout.dart';
import 'package:learn_flutter_01/day02/SFDetail.dart';
import 'package:learn_flutter_01/day02/SFUnknownPage.dart';
import 'package:learn_flutter_01/day02/router/SFRouter.dart';

main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      routes: SFRouter.routers,
      initialRoute: SFRouter.initialRoute,
      onGenerateRoute: SFRouter.generateRoute,
      onUnknownRoute: SFRouter.unknownRoute,
    );
  }
}

class SFHomePage extends StatefulWidget {

  static const String routeName = "/";

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

class _SFHomePageState extends State<SFHomePage> {

  String homeMessage = "default message";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("首页"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(homeMessage,style: TextStyle(fontSize: 20)),
            RaisedButton(
              child: Text("跳转到详情页面"),
              onPressed: () {
                goToDetail(context);
              },
            ),
            RaisedButton(
              child: Text("跳转到关于页面"),
              onPressed: () {
                goToAbout(context);
              },
            ),
            RaisedButton(
              child: Text("跳转到详情页面"),
              onPressed: () {
                goToDetail2(context);
              },
            ),
            RaisedButton(
              child: Text("跳转到设置页面"),
              onPressed: () {
                goToAbou2(context);
              },
            )
          ],
        )
      )
    );
  }

  void goToDetail(BuildContext ctx) {
    Future result = Navigator.of(ctx).push(MaterialPageRoute(
      builder: (ctx) {
        return SFDetail("liyanyan");
      }
    ));

    result.then((value) {
      print(value);
      setState(() {
        homeMessage = value;
      });
    });
  }

  void goToAbout(BuildContext ctx) {
    Future result = Navigator.of(context).pushNamed(SFAbout.routeName,arguments: "liyanyan");

    result.then((value) {
      print(value);
    });
  }

  void goToDetail2(BuildContext ctx) {
    Navigator.of(context).pushNamed(SFDetail.routeName,arguments: "123");
  }

  void goToAbou2(BuildContext ctx) {
    ///setting为定义的页面 跳转页面会直接报错
    Navigator.of(context).pushNamed("/settings");
  }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容