Flutter学习4-Flutter基础能力

导航与路由

  • 导航就是从A页面跳转到B页面
  • 路由是导航的一种方式,通过配置的路由名称进行导航 下面示例中配置了页面的跳转动画和路由map
class NavigatorApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
        pageTransitionsTheme: PageTransitionsTheme(//配置转场动画
          builders: <TargetPlatform, PageTransitionsBuilder>{
            TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
            TargetPlatform.android: CupertinoPageTransitionsBuilder(),
          },
        ),
      ),
      routes: <String, WidgetBuilder>{//配置路由名称
        'app_main': (context) => HomePageState(),
        'learn_start': (context) => LearnWidget(),
        'less_group': (context) => LessGroupPage(),
        'page1': (context) => Page1App(),
      },
      home: NavigatorHomePage(),
    );
  }
}
复制代码

需要注意的是routes界面配置的路由名称是app全局的,别的地方如果配置了相同的路由名称会被覆盖,我们一般会单独建一个文件保存这些路径

app_route.dart

const pageA = "app_main";
const pageB = "learn_start";
const pageC = "less_group";
const pageD = "page1";

var  RoutePath = {
  "$pageA": (context) => HomePageState(),
  "$pageB": (context) => LearnWidget(),
  "$pageC": (context) => LessGroupPage(),
  "$pageD": (context) => Page1App(),
};
复制代码

路由的几种方式

通过名称导航(该名称实际上就是路由了)

方式一:
Navigator.of(context).pushNamed(routeName);
方式二:
Navigator.pushNamed(context, routeName);
复制代码

这时候我们用如下方式

Navigator.of(context).pushNamed(pageA)
复制代码

pageA是我们配置好的路由名称就能跳转了

通过页面(widget)导航

//方式一:
Navigator.of(context).push(MaterialPageRoute(builder:  (context) => HomePageState()));
//方式二:
Navigator.push(context, MaterialPageRoute(builder: (context) => HomePageState()));
复制代码

关闭页面

 Navigator.pop(context);
复制代码

如果你关闭页面的时候出现了黑屏,应该是当前页面和被打开页面配置了多个MaterialApp

一般一个App配置一个MaterialApp即可,剩下的页面可以单独使用Scaffold

Flutter中监听用户手势

GestureDetector

GestureDetector:官方定义为一个检测用户手势widget

  _buildColumn(){
   return Column(
     children: [
       GestureDetector(
         onTap: () => print("onTap"),
         onDoubleTap: () => print("onDoubleTap"),
         onLongPress: () => print("onLongPress"),
         onTapCancel: () => print("onTapCancel"),
         onTapUp: (e) => print("onTapUp"),
         onTapDown: (e) => print("onTapDown"),
         onPanUpdate: (e) => print("onPanUpdate"),
         child: Container(
           padding: EdgeInsets.all(60),
           decoration: BoxDecoration(color: Colors.green),
           child: Text('点我',
               style: TextStyle(color: Colors.white, fontSize: 30)),
         ),
       ),
     ],
   );
 }
复制代码

点击 onTap

I/flutter ( 2161): onTapDown
I/flutter ( 2161): onTapUp
I/flutter ( 2161): onTap
复制代码

双击 onDoubleTap

I/flutter ( 2161): onDoubleTap
复制代码

长按 onLongPress

I/flutter ( 2161): onTapDown
I/flutter ( 2161): onTapCancel
I/flutter ( 2161): onLongPress
复制代码

滑动 onPanUpdate

I/flutter ( 2161): onTapDown
I/flutter ( 2161): onTapCancel
I/flutter ( 2161): onPanUpdate
复制代码

案例:一个随着手指移动的小球

  • 在onPanUpdate中监听用户手指滑动的距离并更新小球的布局
class GestureState extends State<GesturePage> {
  var string = '';
  double moveX = 0, moveY = 0;//定义小球初始位置

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("如何检测用户手势"),
      ),
      body: FractionallySizedBox(
        widthFactor: 1,
        heightFactor: 1,
        child: Stack(
          children: [
            _buildColumn(),
            Positioned(
                left: moveX,
                top: moveY,
                child: GestureDetector(
                  onPanUpdate: (e) => _doMove(e),//监听用户手指移动并更新小球位置
                  child: Container(//小球布局
                    width: 70,
                    height: 70,
                    decoration: BoxDecoration(
                        color: Colors.pinkAccent,
                        borderRadius: BorderRadius.circular(35)),
                  ),
                )),
          ],
        ),
      ),
    );
  }
  _buildColumn();
  _doMove(DragUpdateDetails e) {
    setState(() {
      moveX += e.delta.dx;//这个是每次手指移动的位置,不累加
      moveY += e.delta.dy;
    });
  }
}
复制代码

e.delta.dx和e.delta.dy:是不累加的,是该次位置的距离 效果

Flutter中导入资源和使用资源

以图片资源为例,

1.新建images文件夹并将图片导入

2. 在pubspec.yaml配置图片路径

flutter:
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  # assets:
  #   - images/a_dot_burr.jpeg
  #   - images/a_dot_ham.jpeg
  assets:
    - images/
#    - images/cat.jpg
#    - images/tiger.jpg
#    - images/strawberry.jpg
复制代码

可以整体导入,也可以单独文件导入 路径和格式千万不能配置出错,比如空格,缩进等....要不会有很多坑

使用资源文件

使用时注意是使用全路径+名称

class ResourceState extends State<ResourcePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter中引用资源'),
      ),
      body: Container(
        margin: EdgeInsets.all(20),
        child:Row(
          children: [
            Image.asset('images/car.jpg',width: 100, height: 100,fit: BoxFit.cover),
            Image.asset('images/cat.jpg',width: 100, height: 100,fit: BoxFit.cover),
            Image.asset('images/tiger.jpg',width: 100, height: 100,fit: BoxFit.cover),
          ],
        ),
      ),
    );
  }
}
复制代码

Flutter 打开第三方应用

在我们的日常开发中需要打开浏览器,打电话或者别的指定app,我们需要用到官方提供的插件

url_launcher
复制代码

将该库引入项目,并导入其文件即可实现相关功能

使用

class LaunchState extends State<LaunchPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter打开第三方应用'),
      ),
      body: Center(
        child: Column(
          children: [
            RaisedButton(
              onPressed: _openUrl,
              child: Text('打开浏览器'),
            ),
            RaisedButton(
              onPressed: _openJingDong,
              child: Text('打开京东'),
            ),
          ],
        ),
      ),
    );
  }
  //打开浏览器
  _openUrl() async{
    const url="https://flutter.cn";
    if(await canLaunch(url)){
      await launch(url);
    }else{
      throw 'Could not launch $url';
    }
  }
  //打开京东某个页面
  _openJingDong() async{
    const id="100016437630";
    const url="openapp.jdmobile://virtual?params=%7B%22sourceValue%22:%220_productDetail_97%22,%22des%22:%22productDetail%22,%22skuId%22:%22$id%22,%22category%22:%22jump%22,%22sourceType%22:%22PCUBE_CHANNEL%22%7D";
    if(await canLaunch(url)){
      await launch(url);
    }else{
      throw 'Could not launch $url';
    }
  }
}
复制代码

点击按钮之后即可打开京东

Flutter页面生命周期

由于StatelessWidget 生命周期简单就两个生命周期,不在赘述

StatefullWidget根据阶段可以将其分为:

1. 初始化阶段

  • createState():

当我们构建一个新的StatefulWidget时,这个会立即调用,并且这个方法必须被覆盖

  • initState():

这是创建widget时调用的除构造方法外的第一个方法:类似于Android的:onCreate() 与iOS的 viewDidLoad(),在这个方法中通常会做一些初始化工作,比如channel的初始化,监听器的初始化等

2. 更新阶段

  • didChangeDependencies():

当依赖的State对象改变时会调用,在第一次构建widget时,在initState()之后立即调用此方法;如果的StatefulWidgets依赖于InheritedWidget,那么当当前State所依赖InheritedWidget中的变量改变时会再次调用它

拓展:InheritedWidget可以高效的将数据在Widget树中向下传递、共享,可参考:book.flutterchina.club/chapter7/in…

  • build()

这是一个必须实现的方法,在这里实现你要呈现的页面内容,它会在在didChangeDependencies()之后立即调用,另外当调用setState后也会再次调用该方法

  • didUpdateWidget()

这是一个不常用到的生命周期方法,当父组件需要重新绘制时才会调用;该方法会携带一个oldWidget参数,可以将其与当前的widget进行对比以便执行一些额外的逻辑

3. 销毁阶段

  • deactivate()

很少使用,在组件被移除时调用在dispose之前调用

  • dispose()

组件被销毁时调用,通常在该方法中执行一些资源的释放工作比如,监听器的卸载,channel的销毁等

Flutter App生命周期

  • WidgetsBinding.instance.addObserver()通过该方法监听生命周期方法
  • WidgetsBinding.instance.removeObserver()通过该方法,移除生命周期 flutter应用的生命周期,在各个平台上有所差异
enum AppLifecycleState {
  resumed,//应用进去前台
  inactive,//应用进入后台
  paused,//不常用:应用程序处于非活动状态,并且未接收用户输入时调用,比如:来了个电话
  detached,//不常用:应用程序被挂起是调用,它不会在iOS上触发
}
复制代码

示例

class AppLifeState extends State<AppLifeCirclePage> with WidgetsBindingObserver {

  @override
  void initState() {
    WidgetsBinding.instance.addObserver(this);//注册监听
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('App生命周期'),
      ),
      body: Container(),
    );
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    print('state = $state');
    if(state==AppLifecycleState.resumed){
      //应用进去前台
    }else if(state==AppLifecycleState.paused){
      //应用进入后台
    }else if(state==AppLifecycleState.detached){
      //不常用:应用程序被挂起是调用,它不会在iOS上触发
    }else if(state==AppLifecycleState.inactive){
      //不常用:应用程序处于非活动状态,并且未接收用户输入时调用,比如:来了个电话
    }
  }
  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);//移除监听
    super.dispose();
  }
}
复制代码

Flutter主题和字体设置

修改flutter应用主题

通过给MaterialApp{}中brightness 动态修改其值即可

 var _theme=Brightness.light;
 MaterialApp{
 theme: ThemeData(
 brightness: _theme,
 )
 }
 
 //修改
   setState(() {
     if(_theme==Brightness.light){
         _theme=Brightness.dark;
       }else{
          _theme=Brightness.light;
             }
       });
复制代码

修改Flutter字体

  1. 将字体的ttf添加到项目中

  2. 将字体配置到项目中 打开pubspec.yaml,添加字体配置(注意缩进和格式)

pubspec.yaml文件
flutter:

  fonts:
    - family: StarFall//字体名称
      fonts:
        - asset: fonts/StarFall.ttf//字体资源文件
复制代码
  1. 使用字体
  • 全局字体
  • 某文字字体
Text(
     '动态修改flutter主题',
     style: TextStyle(fontFamily: 'StarFall'),
     )
复制代码

使用字体中的icon

Icon(IconData(0x88ff8,fontFamily: "StarFall"))
复制代码

其中值:0x88ff8是字体库中自带的icon索引

学习flutter的常用项目

FlutterExampleApps

flutter官方demo

flutter-samples

Flutter项目常用插件

-打开第三方应用

url_launcher

分类:
Android
标签: