跳至主要內容

路由

Emilia Zhen大约 4 分钟flutter

页面跳转

  • Navigator.push跳转到下一个页面,接收2个参数,一个是上下文,另一个是跳转的函数
  • Navigator.pop返回到上一个页面,使用时传递一个上下文,必须上级页面使用了.push才能使用

导航参数传递使用Navigator控件,然后使用路由MaterialPageRoute传递参数,并在子页面接收参数

itemBuilder: (context,index){
  return ListTile(
    title: Text(items[index].title),
    onTap: () async{
       final res = await Navigator.push(
         context,
         MaterialPageRoute(
            builder: (context) => new GoodsDetail(item:items[index])
         )
       );
       Scaffold.of(context).showSnackBar(SnackBar(content:Text('$res')));
    },
  );
},
....
children: <Widget>[
  Text('${item.description}'),
  RaisedButton(
    child: Text('选择该商品'),
    onPressed: (){
      Navigator.pop(context,'已选择${item.title}');
    },
  )
],
....

Dart的异步请求和等待和ES6中的方法很像,直接使用async...await就可以实现,跳转的时候是异步的,等结果回来后使用SnackBar提示信息控件,SnakeBar是以ScaffoldshowSnackBar方法来进行显示的,在Navigator.pop(context,'xxx'),第二个参数就是返回的数据

路由

void main(){
  runApp(MaterialApp(
    home: MyAppHome(),
    routers: <String, WidgetBuilder>{
      '/a': (BuildContext context) => MyPage(title: 'pageA'),
      '/b': (BuildContext context) => MyPage(title: 'pageB'),
      '/c': (BuildContext context) => MyPage(title: 'pageC'),
    }
  ))
}

通过把路由名字pushNavigator来跳转

Navigator.of(context).pushNamed('/b')

通过使用Navigatorpush方法,该方法将给定route添加到导航器的历史记录中

Navigator.push(context,MaterialPageRoute(builder: (BuildContext context) => UsualNavscreen()))

路由动画

  1. 新建自定义路由动画custom_router.dart
  2. 在页面跳转引入路由动画dart文件,并使用
onPressed: (){
  Navigator.of(context).push(
    CustomRoute(SecondPage())
  );
},

AppBar组件backgroundColor更改背景颜色,elevation属性是滚动时的融合程度,一般有滚动时默认是4.0,我们设置成0.0可以与页面完全融合 3. 编写自定义动画custom_router.dart文件 路由动画的原理重写PageRouteBuildertransitionsBuilder方法

import 'package:flutter/material.dart';
class CustomRoute extends PageRouteBuilder{
  final Widget widget;
  CustomRoute(this.widget):super(
  transitionDuration: const Duration(seconds: 1),
  pageBuilder:(BuildContext context, Animation<double> animation1,Animation<double> animation2){
    return widget;
  },
  transitionsBuilder: (BuildContext context, Animation<double> animation1, Animation<double> animation2, Widget child){
     return FadeTransition(
        opacity: Tween(begin: 0.0, end: 1.0).animate(
           CurvedAnimation(
             parent:animation1,
            curve: Curves.fastOutSlowIn
           ),
         ),
        child:child,
     );
   }
}
  • FadeTransition渐隐渐现过渡效果,主要设置opacity透明度属性
  • animate动画样式,一般使用动画曲线组件CurvedAnimation
  • transitionDuration设置动画持续时间 缩放路由动画
return ScaleTransition(
  scale:Tween(begin: 0.0, end: 1.0).animate(
    CurvedAnimation(
      parent:animation1,
      curve: Curves.fastOutSlowIn
    )
  ),
  child:child
);

旋转缩放路由动画

return RotationTransition(
  turns: Tween(begin:0.0, end:1.0).animate(
    CurvedAnimation(
      parent:animation1,
      curve: Curves.fastOutSlowIn
    )
  ),
  child: ScaleTransition(
    scale:Tween(begin: 0.0, end:1.0).animate(
      CurvedAnimation(
        parent: animation1,
        curve: Curves.fastOutSlowIn
      )
    ),
    child: child,
  ),
);

左右滑动路由动画

return SlideTransition(
  position: Tween<Offset>(
    begin: Offset(-1.0, 0.0),
    end: Offset(0.0, 0.0)
  ).animate(
     CurvedAnimation(
        parent: animation1,
        curve: Curves.fastOutSlowIn
      )
   ),
   child: child,
);

右滑返回上一页

import 'package:flutter/cupertino.dart';
class RightBackDemo extends StatelessWidget {
     @override
      Widget build(BuildContext context){
           return CupertinoPageScaffold(
                 child:Center(
                      child: Container(
                           height: 100.0,
                            width: 100.0,
                          color: CupertinoColors.activeBlue,
                          child: CupertinoButton(
                                child: Icon(CupertinoIcons.add),
                                onPressed: (){
                                      Navigator.of(context).push(CupertinoPageRoute(
                                            build: (BuildContext context){
                                                  return RightBackDemo
                                            }
                                       ))
                                }
                           )
                      )
                   )
            );
     }
}

Flutter有两套UI模版,一套material另一套是Cupertino

闪屏动画

AnimationControllerAnimation的一个子类,它可以控制Animation,可以控制动画比如动画执行时间
vsync:this 垂直同步设置,使用this就行了
duration 动画持续时间,可以使用seconds秒,也可以使用milliseconds毫秒

import 'package:flutter/material.dart';
import 'home_page.dart';
class SplashScreen extends StatefulWidget {
  _SplashScreenState createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation _animation;
  void initState(){
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 3000)
    );
    _animation = Tween(begin: 0.0,end: 1.0).animate(_controller);
    _animation.addStatusListener((status){
      if(status == AnimationStatus.completed){
        Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(
                     builder: (context) => MyHomePage()),
                    (route) => route==null
                  );
      }
    });
       // 播放动画
    _controller.forward();
  }
  @override
  void dispose(){
    _controller.dispose();
    super.dispose();
  }
  @override
  Widget build(BuildContext context){
    return FadeTransition( // 透明度动画控件
      opacity: _animation, // 执行动画
      child: Image.network(
                   'xx',
        scale: 2.0,
        fit: BoxFit.cover,
      ),
    );
  }
}

animation.addStatusListener动画事件监听器,可以监听动画的执行状态
AnimationStatus.completed表示动画已经执行完毕
pushAndRemoveUntil跳转页面兵销毁当前控件

fluro

Flutter路由解决方案

  1. handler相当于一个路由规则,在routers文件夹下新建router_handler.dart
import 'package:flutter/material.dart';
import 'package:fluro/fluro.dart';
import '../pages/goods_detail_page.dart';
Handler goodsDetailHandler = Handler(
  handlerFunc: (BuildContext context,Map<String,List<String>> params){
    String goodsId = params['id'].first;
    print('detailId is $goodsId');
    return GoodsDetailPage(goodsId);
  }
);
  1. handler只是对每个路由的独立配置,还需对路由有个总体的配置,在routers文件夹下新建routes.dart
import 'package:flutter/material.dart';
import 'package:fluro/fluro.dart';
import './router_handler.dart';
class Routers {
  static String root = '/';
  static String goodsDetailPage = '/goodDetail';
  static void configureRoutes(Router router){
    router.notFoundHandler = new Handler(
       handlerFunc: (BuildContext context,Map<String,List<String>> params){
          print('>>>>>>>> Router not Found');
       }
    );
    router.define(goodsDetailPage,handler:goodsDetailHandler);
  }
}
  1. 为了使用方便,直接把Router进行静态化,这样在任何一个页面都可以直接进行使用了。在routers文件夹下新建Staticize.dart
import 'package:fluro/fluro.dart';
class Staticize {
  static Router router;
}
  1. main.dart文件里进行全局注入
import 'package:fluro/fluro.dart';
import './routers/staticize.dart';
import './routers/routers.dart';
….
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final router = Router();
    Routers.configureRoutes(router);
    Staticize.router = router;
    return Container(
      child: MaterialApp(
         title: 'LifeShop',
         onGenerateRoute: Staticize.router.generator,
         debugShowCheckedModeBanner: false,
         theme: ThemeData(
            primaryColor: Colors.pink
          ),
          home: IndexPage(),
       ),
    );
  }
}
  1. 在需要跳转路由的页面中使用
import '../routers/staticize.dart';
….
onTap: (){
  Staticize.router.navigateTo(context, '/goodDetail?id=${newList[index].goodsId}');
},

hero 动画

class PhotoHero extends StatelessWidget {
  const PhotoHero({ Key key, this.photo, this.onTap, this.width }) : super(key: key);
  final String photo;
  final VoidCallback onTap;
  final double width;
  Widget build(BuildContext context) {
    return new SizedBox(
      width: width,
      child: new Hero(
        category: photo,
        child: new Material(
          color: Colors.transparent,
          child: new InkWell(
            onTap: onTap,
            child: new Image.asset(
              photo,
              fit: BoxFit.contain,
            ),
          ),
        ),
      ),
    );
  }
}

class HeroAnimation extends StatelessWidget {
  Widget build(BuildContext context) {
    timeDilation = 5.0; // 1.0 means normal animation speed.
    return new Scaffold(
      appBar: new AppBar(
        title: const Text('Basic Hero Animation'),
      ),
      body: new Center(
        child: new PhotoHero(
          photo: 'images/flippers-alpha.png',
          width: 300.0,
          onTap: () {
            Navigator.of(context).push(new MaterialPageRoute<Null>(
              builder: (BuildContext context) {
                return new Scaffold(
                  appBar: new AppBar(
                    title: const Text('Flippers Page'),
                  ),
                  body: new Container(
                    // The blue background emphasizes that it's a new route.
                    color: Colors.lightBlueAccent,
                    padding: const EdgeInsets.all(16.0),
                    alignment: Alignment.topLeft,
                    child: new PhotoHero(
                      photo: 'images/flippers-alpha.png',
                      width: 100.0,
                      onTap: () {
                        Navigator.of(context).pop();
                      },
                    ),
                  ),
                );
              }
            ));
          },
        ),
      ),
    );
  }
}