Flutter 和 Dart 开发中一些技巧和坑点填坑指南详解

在这里插入图片描述

开篇引入一个 Flutter 的所跨平台的图片。图中大概列了一下 Flutter 未来所跨的平台, Flutter 有 Google Flutter 技术团队的不断迭代开发,相信Flutter将是未来的主流跨平台开发框架。本节课笔者将会根据近一段时间的 Flutter 的开发实践和技术研究实践进行一个总结和整理,将 Flutter 和 Dart 开发中遇到的一些难点、问题、技巧和解决方案给大家分享一下,避免大家遇到相同的问题耽误时间,降低开发和学习成本,做到事半功倍、提升开发和学习效率。接下来大家一起学习吧。本文主要介绍:

  • Flutter 和 Dart 简介
  • Flutter 和 Dart 开发实践中的技巧和难点
  • Flutter 和 Dart 的未来展望

Flutter 和 Dart 简介

在这里插入图片描述
Flutter 是 Google 推出并开源的移动端开发框架(基于 Dart 语言)。Dart 是由 Google 开发的一种面向对象编程的强类型语言,语法有点像 Java 与 JavaScript 的集合体。Flutter 使用 Skia 作为2D渲染引擎,也就是我们看到的界面是通过 Skia 进行绘制渲染出来的。Skia 是 Google 的一个2D图形处理函数库,并且 Skia 是跨平台的。目前 Google Chrome 浏览器和Android均采用Skia作为其绘图引擎。由于 Android 系统已经内置了 Skia ,所以 Flutter 在打包 Android 应用时不需要再将 Skia 打入 APK 中,但 IOS 系统并未内置 Skia ,所以构建 IOS 应用时,也必须将 Skia 一起打包,这也是为什么 Flutter 应用的 Android 安装包比IOS的安装包小的主要原因。

其实目前的鸿蒙系统也是类似 Flutter 和 Fuchsia OS,只不过 Flutter 和 Dart 推出的比较早,技术也比较成熟。未来的 Flutter 将全面跨主流平台:Android、IOS、Windows(研发中)、Mac(研发中)、Linux(研发中)、Fuchsia OS(研发中)、Web(研发中)、物联网系统(研发中)、后端、前端等等。
其中很多人期待的Web SDK应该今年内左右可能会发布一个比较完善的版本。

Flutter 有 Google 团队的支持,目前开发者数量也指数级在上升。目前很多大公司都有应用 Flutter 进行项目的开发,如 Google 、阿里巴巴、京东、腾讯、 Square 等公司都有应用。Flutter的独特的优势已经吸引了大量的开发者和公司进行学习和使用,并且Flutter的社区、文档、SDK更新频率等相关资源越来越完善和强大,我们有理由去学习Flutter和Dart。希望大家通过本门课程有很大的提升和收获,一起努力,一起学习,一起进步。
在这里插入图片描述
目前 Flutter 的最新版本是1.7:
在这里插入图片描述
Flutter 基础架构图:
在这里插入图片描述
Flutter 详细架构图:
在这里插入图片描述
Flutter 跨平台特点:
目前我们在开发应用时,需要同时兼容 iOS 和 Android 两种平台时有两种技术选择:走原生开发路线,把界面和逻辑在不同平台分别实现;抑或用同一套代码兼容多个平台,但这往往意味着运行速度和产品体验的损失。除了原生外,目前跨平台技术一般是混合开发,如采用H5、React Native、Weex、小程序等技术实现跨平台应用。不过这些或多或少都能感觉到卡顿和体验不流畅,并且开发和学习成本非常高,而且都有各自的局限性。Flutter的出现就是为我们提供了一套两全其美的解决方案:既能用原生代码直接调用的方式来加速图形渲染和 UI 绘制,又能同时运行在两大主流移动操作系统上,并且体验和流畅度和原生基本一致、开发效率也非常高、学习难度和成本低。那么接下来看下几种方案的对比情况:

技术 性能 开发效率 渲染方式 学习成本 可扩展性
Flutter 高,接近原生体验 Skia高性能自绘引擎 低,Widget组件化 高,采用插件化的库进行扩展
RN/Weex/小程序 有延迟,一般 一般,复杂、效率低 Js驱动原生渲染 高,复杂 一般
原生应用 一般 原生渲染 高,需要学习Android和IOS原生API

从上面的对比可以看出,Flutter的优势明显:高体验度、高开发效率、低学习成本、高可扩展性、未来Google Flutter团队还将使Flutter支持PC和Web的跨平台开发等。 在Flutter 1.0正式版本尚未推出之前,已经有成百上千的基于 Flutter 开发的应用在 Apple Store 和 Google Play 上架,相信Flutter将会被越来越多的开发者和公司所采用和接受。

最后我们回顾下2018年和2019年Flutter的发展情况:

  • 2 月底在世界移动大会 (MWC) 上宣布了第一个 Beta 版发布;
  • 5 月的 Google I/O 大会上发布了 Beta 3;
  • 6 月底的 GMTC 宣布了首个发布预览版;
  • 9 月的谷歌开发者大会 (Google Developer Days) 上,我们宣布 发布预览版 2 发布。
  • 12 月宣布发布正式稳定1.0版;
  • 2019年 2 月宣布发布稳定版1.2版本SDK。
  • 2019年 5 月宣布发布稳定版1.5版本SDK。

目前Flutter的社区非常活跃,Flutter 在 Github 最受欢迎的开源软件中排名前 50,国内也有大量的开发者开始使用 Flutter 构建跨平台 (Android & iOS) 的应用,如:阿里巴巴、腾讯、京东等都使用 Flutter 发布了自己的应用。Google官方Flutter团队计划Flutter未来也将支持Flutter Web和Flutter PC的应用移植开发,让我们拭目以待吧!

好了,关于 Flutter 的详细介绍我们就讲到这里,大家应该都对 Flutter 和 Dart 都非常熟悉了。接下来咱们就开门见山,把遇到的一个一个的问题和对应的解决方案进行讲解分享。

Flutter 和 Dart 开发实践中的技巧和难点

本文讲解使用的是 Windows 10 开发环境,编辑器 IDE 使用 Android Studio 和 Visual Studio Code 。

1、SDK下载和升级慢?

可以看到主要有dev、beta和stable三个官方分支,这里正式开发的话可以下载stable稳定版本。遇到SDK升级和下载慢的话,可以使用配置国内镜像环境变量:
可以将如下的国内下载镜像地址加入到环境变量中:

变量名:PUB_HOSTED_URL,变量值:https://pub.flutter-io.cn
变量名:FLUTTER_STORAGE_BASE_URL,变量值:https://storage.flutter-io.cn

Flutter SDK环境变量,将Flutter的bin目录加入环境变量即可:

[你的Flutter文件夹路径]\flutter\bin

配置完后,可以使用flutter doctor命令,它可以帮助我们检查Flutter环境变量是否设置成功,Android SDK是否下载以及配置好环境变量等等。如果有相关的错误提示,根据提示进行修复和安装、设置即可。每次运行这个命令,都会帮你检查是否缺失了必要的依赖。通过运行 flutter doctor 命令来验证你是否已经正确地设置了,并且可以自动更新和下载相关的依赖。

2、模拟器使用不方便,Android模拟器无法连接安装应用?

如遇到Error connecting to the service protocol:HttpException: Connection closed before full header was received,uri= http://127.0.0.1:1076/...类似的错误的话,一般是由于你使用了Android Q(API 29)的模拟器导致的,目前Android Q模拟器对Flutter支持有些问题,所以建议遇到这个问题的话,使用Anroid Q(API 29)以下的版本的模拟器。

模拟器置顶。
我们的Android原生模拟器是支持置顶的:
在这里插入图片描述
勾选就可以置顶了。

模拟器快速启动。
当我们通过Android Studio的AVD Manager新建了一个模拟器后,我们后续就可以通过建立一个bat文件快速启动模拟器了。在这个bat文件中写入启动模拟器的命令,这样每次启动模拟器直接运行这个bat文件即可:

D:\Sdk\emulator\emulator.exe -avd Pixel_XL_API_28

模拟器所在的SDK目录根据你的实际情况位置修改即可。使用时双击这个bat文件就可以运行模拟器了。

3、Flutter 和 Dart 升级出错?

关于 Flutter 和 Dart 升级的话,我们可以通过flutter upgrade命令进行升级。当遇到提示需要使用 Power Shell 5.0及以上版本的话,说明你的机器是 windows 7系统或者 Power Shell 版本低于5.0,这里你或者升级Power Shell版本,或者升级 Windows 操作系统到 Windows 10,当然最好是直接去 Flutter 和 Dart 官方分别直接下载最新版本 SDK 的压缩包解压覆盖旧版本即可。

4、遇到Waiting for another flutter command to release the startup lock…?

此时需要打开Flutter SDK的bin目录:flutter/bin/cache/lockfile,删除这个文件就行了。

5、Flutter引入资源图片和字体等的使用?

Flutter的应用内资源图片和字体等的使用,必须要在pubspec.yaml配置文件里进行配置才可以使用。

//项目名称:要用英文,类似于Android中的包名,如果它修改了整个项目的引入的路径都要修改
//所以一般确定了就不要修改
name: flutter_samples
//项目描述
description: A new Flutter project.

//版本号,这个会覆盖对应Android和IOS的应用版本号
//+号前对应Android的versionCode,+号后对应Android的versionName
//+号前对应IOS的CFBundleVersion,+号后对应IOS的CFBundleShortVersionString
version: 1.0.0+1

//表示项目的编译环境要求为dart sdk版本号在2.1.0和3.0.0之间
environment:
  sdk: ">=2.1.0 <3.0.0"


//项目的依赖插件库
//Flutter插件库在这里查找引用:https://pub.dartlang.org/flutter
dependencies:
  flutter:
    sdk: flutter
//我们可以在这里引入插件库
  cupertino_icons: ^0.1.2
  flutter_webview_plugin: ^0.3.1

dev_dependencies:
  flutter_test:
    sdk: flutter

//flutter相关配置
flutter:
//是否使用material图标,建议为true
  uses-material-design: true

  //配置项目文件里的图片路径
  //如果需要使用项目目录内附带的图片、音视频等资源,必须在这里配置定义
  assets:
    - images/a_dot_burr.jpeg
    - images/a_dot_ham.jpeg

  //字体文件资源相关配置
  fonts:
    - family: Schyler
      fonts:
        - asset: fonts/Schyler-Regular.ttf
        - asset: fonts/Schyler-Italic.ttf
          style: italic
    - family: Trajan Pro
      fonts:
        - asset: fonts/TrajanPro.ttf
        - asset: fonts/TrajanPro_Bold.ttf
          weight: 700

//下面这几项一般只有在编写插件库发布到Dart Pub时才写,一般不用写  
//作者
authors:
- Natalie Weizenbaum <[email protected]>
- Bob Nystrom <[email protected]>
//主页
homepage: https://example-pet-store.com/newtify
//文档地址
documentation: https://example-pet-store.com/newtify/docs
//发布到
publish_to: none

如果遇到配置完都不可以使用的情况,请注意配置文件的缩进和格式,是不是多了或者少了一个空格导致的,同时也要注意路径是否正确等。

6、Flutter打包成release版本后,安装不能联网和访问文件?

可能是没有添加相应的访问权限,Android应用需要在AndroidManifest.xml里添加相应的权限,并且注意Android 6.0后部分危险分类内的权限需要主动申请才可以使用。具体的权限名称和如何申请权限大家可以自行百度或者使用第三方插件库实现申请权限功能。

7、Dart中var、dynamic、Object的区别和关系?

Object 是 Dart 所有对象的基类,也就是说所有类型都是 Object 的子类。所以任何类型对象都可以声明为 Object 类型,但是一般不这么用。一般声明为 var 或者 dynamic 类型。

var 它可以接收任何类型的变量,但最大的不同是 Dart 中 var 变量一旦赋值,类型便会确定,后面则不能再改变其类型。

dynamic和var相似,只不过它声明的类型,后续可以进行修改其类型。

8、Dart中final和const的区别和关系?

final 和 const 所声明的变量只能赋值一次,后续不能重新赋值更改。final 和 const 不是var,也不是一个类型。使用了 final 和 const 修饰的变量类型声明可以省略,并且不可以与var同时使用。类级别的常量,通常用 static const 来声明。

两者区别在于:const 变量是一个编译时常量,编译时必须有一个确定的值;final 是运行时常量,运行时有一个确定的值即可。举个例子:

final dt = DateTime.now();//正确,运行时有确定的值
 
const dt = const DateTime.now();//错误,需要编译时有确定的值

9、Dart中异步操作:Future、Stream及async和await的使用?

跟其他平台一样,Flutter 和 Dart 中也有异步操作函数。

返回为 Future 或者 Stream 对象的函数,这些函数被称为异步函数。例如返回的Future对象可以方便我们进行后续的链式调用和操作,类似于 RxJava 和 Promise 。举个例子:

Future.delayed(new Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");
}).then((data){
   //执行成功会走到这里 
   print(data);
}).catchError((e){
   //执行失败会走到这里   
   print(e);
}).whenComplete((){
   //无论成功或失败都会走到这里
});

多个异步的操作:

Future.wait([
  // 2秒后返回结果  
  Future.delayed(new Duration(seconds: 2), () {
    return "hello";
  }),
  // 4秒后返回结果  
  Future.delayed(new Duration(seconds: 4), () {
    return " world";
  })
]).then((results){
  print(results[0]+results[1]);
}).catchError((e){
  print(e);
});

当所有任务都执行完毕后才一起返回结果。

Dart 中的 async/await 和 JavaScript 中的 async / await 功能和用法基本是一模一样的。需要配合一起使用:

task() async {
   try{
    String id = await login("alice","******");
    String userInfo = await getUserInfo(id);
    await saveUserInfo(userInfo);
    //执行接下来的操作   
   } catch(e){
    //错误处理   
    print(e);   
   }  
}

async 修饰方法名为异步,await 为内部的耗时操作进行标记。

Stream 也是用于接收异步事件数据,和Future 不同的是,它可以接收多个异步操作的结果。 举个例子:

Stream.fromFutures([
  // 1秒后返回结果
  Future.delayed(new Duration(seconds: 1), () {
    return "hello 1";
  }),
  // 抛出一个异常
  Future.delayed(new Duration(seconds: 2),(){
    throw AssertionError("Error");
  }),
  // 3秒后返回结果
  Future.delayed(new Duration(seconds: 3), () {
    return "hello 3";
  })
]).listen((data){
   print(data);
}, onError: (e){
   print(e.message);
},onDone: (){

});

我们的listen里会输出每个任务结束后的结果。如果有3个任务,它就会分3次输出返回的结果。

10、Android启动有一个白屏闪现怎么处理?

在android/app/src/main/res/drawable/launch_background.xml中定义了自定义修改启动页splash的方法:

<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@android:color/white" />

    <!-- You can insert your own image assets here -->
    <!-- <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/launch_image" />
    </item> -->
</layer-list>

我们可以将白色修改为透明色,或者更换为一张图片都可以。

11、Flutter如何获取屏幕宽高信息?

Flutter的屏幕宽高等信息都是通过MediaQuery.of(context).size.来获取的:

double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;

12、Flutter布局浸入到了手机状态栏如何处理?

使用SafeArea包裹一下布局最外层即可:

SafeArea(top: true,
    child: MaterialApp(
        home: ,
    ),);

13、Flutter如何全屏和取消全屏?

全屏:

SystemChrome.setEnabledSystemUIOverlays([]); 

取消全屏:

SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.top, SystemUiOverlay.bottom]);

14、Flutter如何设置屏幕支持的方向?

// 设置竖屏
SystemChrome.setPreferredOrientations([
          DeviceOrientation.portraitUp,
          DeviceOrientation.portraitUp,
        ]);
// 设置横屏
SystemChrome.setPreferredOrientations([
          DeviceOrientation.landscapeLeft,
          DeviceOrientation.landscapeLeft,
        ]);

15、Flutter如何取消和设置标题栏左侧的图标?

设置左侧图标:

appBar: AppBar(
        leading: Icon(Icons.menu),
        automaticallyImplyLeading: true,)

取消左侧图标:

appBar: AppBar(
        leading: null,
        automaticallyImplyLeading: false,)

16、Flutter布局或者文字超过边界被裁剪了怎么处理?

如果控件超出屏幕范围后想自动换行,可以尝试使用Wrap组件进行包裹使用。
如果不行的话,例如Row里的两个子控件,我们可以尝试将Row里的子控件用Expanded包裹起来,这样就可以实现超过屏幕自动换行不被裁剪了。

17、Flutter右上角有个debug标志,如何去掉?

这个是调试模式下默认自带的,当正式打成release包时就没有了。如果你想在开发时候也不显示这个标志,只需要在main.dart里配置一个属性即可:

MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.teal,
        ),
        home: ShowAppPage(),
        routes: <String, WidgetBuilder>{
          '/buttonpage': (BuildContext context) => ButtonSamples(),
          '/routepage': (BuildContext context) => RouteSamples(),
        },
        // 设置为false就不现实debug标志了
        debugShowCheckedModeBanner: false,
      )

18、Flutter如何动态控制显示和隐藏某个布局或组件?

可以使用Offstage组件,动态控制其offstage属性值即可实现这个效果。

19、Flutter如何禁止GridView类似的列表组件禁止滚动或添加滚动回弹效果?

Flutter 中禁用 GridView 的滚动,可以使用 physics 属性,取值为NeverScrollableScrollPhysics()
如果添加滚动回弹效果依然是设置 physics 属性,取值为BouncingScrollPhysics()

20、Flutter如何监听某个组件已经渲染完毕?

监听某个组件是否已经渲染完成,使用 WidgetsBinding ,方法是在 initstate 或者 build 中注册回调:

WidgetsBinding.instance.addPostFrameCallback((callback){
      print("complete");
    });

21、Flutter如何设置定时任务,定时器?

    // 开始计时
   _startTimer(){
   var _timer = Timer.periodic(new Duration(seconds: 1), (timer){
       // 编写自己的逻辑
      setState(() {});
    });
  }
    // 取消计时
  _cancleTimer(){
    _timer?.cancel();
  }

22、Flutter如何监听按键?

Flutter监听按键用RawKeyboardListener:

const RawKeyboardListener({
    Key key,
    @required this.focusNode,//焦点结点
    @required this.onKey,//按键接收处理事件
    @required this.child,//接收焦点的子控件
  })

举个例子:

FocusNode focusNode0 = FocusNode();

... ...

RawKeyboardListener(
      focusNode: focusNode0,
      child: Container(
        decoration: getCircleDecoration(color0),
        child: Padding(
          child: Card(
            elevation: 5,
            shape: CircleBorder(),
            child: CircleAvatar(
              child: Text(''),
              backgroundImage: AssetImage("assets/icon_tv.png"),
              radius: radius,
            ),
          ),
          padding: EdgeInsets.all(padding),
        ),
      ),
      onKey: (RawKeyEvent event) {
        if (event is RawKeyDownEvent && event.data is RawKeyEventDataAndroid) {
          RawKeyDownEvent rawKeyDownEvent = event;
          RawKeyEventDataAndroid rawKeyEventDataAndroid = rawKeyDownEvent.data;
          print("keyCode: ${rawKeyEventDataAndroid.keyCode}");
          switch (rawKeyEventDataAndroid.keyCode) {
            case 19: //KEY_UP
              FocusScope.of(context).requestFocus(_focusNode);
              break;
            case 20: //KEY_DOWN
              break;
            case 21: //KEY_LEFT
              FocusScope.of(context).requestFocus(focusNode4);
              break;
            case 22: //KEY_RIGHT
              FocusScope.of(context).requestFocus(focusNode1);
              break;
            case 23: //KEY_CENTER
              break;
            case 66: //KEY_ENTER
            break;
            default:
              break;
          }
        }
      },
    )

23、Flutter如何设置焦点控制?

Flutter Widget 获取焦点的处理通过 FocusScope 这个 Widget 处理,配合FocusNode。

FocusNode focusNode0 = FocusNode();
... ...
//主动获取焦点
FocusScope.of(context).requestFocus(focusNode0);
//自动获取焦点
FocusScope.of(context).autofocus(focusNode0);

这样就可以进行焦点获取处理了。FocusNode 这个类也很重要,负责监听焦点的工作。

焦点的移动我们用最新的 DefaultFocusTraversal 进行自动指定方向,搜索下一个焦点:

FocusScope.of(context)
                    .focusInDirection(TraversalDirection.up);
// 或者像下面这样使用
DefaultFocusTraversal.of(context).inDirection(
                    FocusScope.of(context).focusedChild, TraversalDirection.up);

DefaultFocusTraversal.of(context)
                    .inDirection(_focusNode, TraversalDirection.right);

支持上下左右四个方向。 如果想手动指定下一个焦点是哪个的话,可以像下面这样用:

FocusScope.of(context).requestFocus(focusNode);

24、Flutter如何监听一些生命周期事件?

我们先了解下生命周期的概念,也就是一个页面对象从创建到销毁的整个状态管理。我们看下Flutter的State生命周期的示意图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ep8pSO4C-1569849191792)(images/flutter_all/state.jpg)]
可以看到我们的一个页面在加载创建时需要执行:
构造函数 -> initState -> didChangeDependencies -> build方法,然后才会渲染为一个页面。

当销毁关闭时:
deactivate -> dispose

内部的前后台页面状态变化主要有:

enum AppLifecycleState {
  // 恢复可见
  resumed,
  // 不可见,后台运行,无法处理用户响应
  inactive,
  // 处在并不活动状态,无法处理用户响应。例如来电,画中画,弹框
  paused,
  // 应用被立刻暂停挂起,ios上不会回调这个状态
  suspending,
}

当页面更新是会执行:
didUpdateWidget -> build
可能会调用多次。

那么接下来通过代码实例来看下Flutter的生命周期:

import 'package:flutter/material.dart';

class StateSamples extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return StateSamplesState();
  }
}

class StateSamplesState extends State<StateSamples>
    with WidgetsBindingObserver {
  //插入渲染树时调用,只调用一次
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  //构建Widget时调用
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('LifeCycleState'),
      ),
      body: Center(
        child: Column(
          children: <Widget>[],
        ),
      ),
    );
  }

  //state依赖的对象发生变化时调用
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
  }

  //组件状态改变时候调用,可能会调用多次
  @override
  void didUpdateWidget(StateSamples oldWidget) {
    super.didUpdateWidget(oldWidget);
  }

  //当移除渲染树的时候调用
  @override
  void deactivate() {
    super.deactivate();
  }

  //组件即将销毁时调用
  @override
  void dispose() {
    super.dispose();
    WidgetsBinding.instance.removeObserver(this);
  }

  //APP生命周期监听
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      //恢复可见
    } else if (state == AppLifecycleState.paused) {
      //处在并不活动状态,无法处理用户响应
      //例如来电,画中画,弹框
    } else if (state == AppLifecycleState.inactive) {
      //不可见,后台运行,无法处理用户响应
    } else if (state == AppLifecycleState.suspending) {
      //应用被立刻暂停挂起,ios上不会回调
    }
    super.didChangeAppLifecycleState(state);
  }

  //其他方法

  //热重载时调用
  @override
  void reassemble() {
    super.reassemble();
  }

  //路由弹出
  @override
  Future<bool> didPopRoute() {
    return super.didPopRoute();
  }

  //新的路由
  @override
  Future<bool> didPushRoute(String route) {
    return super.didPushRoute(route);
  }

  //系统窗口相关改变回调,例如旋转
  @override
  void didChangeMetrics() {
    super.didChangeMetrics();
  }

  //文字缩放大小变化
  @override
  void didChangeTextScaleFactor() {
    super.didChangeTextScaleFactor();
  }

  //本地化语言变化
  @override
  void didChangeLocales(List<Locale> locale) {
    super.didChangeLocales(locale);
  }

  //低内存回调
  @override
  void didHaveMemoryPressure() {
    super.didHaveMemoryPressure();
  }

  //当前系统改变了一些访问性活动的回调
  @override
  void didChangeAccessibilityFeatures() {
    super.didChangeAccessibilityFeatures();
  }

  //平台色调主题变化时
  @override
  void didChangePlatformBrightness() {
    super.didChangePlatformBrightness();
  }
}

25、Flutter如何监听返回按键?

我们看下返回键的监听,返回键监听拦截在Flutter中比较不一样。
是单独使用一个组件:WillPopScope。
接下来就通过一个实例来看下Flutter中实现连按两次返回键退出的效果:

class KeyListenerState extends State<KeyListenerSamples> {
  int last = 0;
  int index = 0;

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    // 要用WillPopScope包裹
    return WillPopScope(
      // 编写onWillPop逻辑
      onWillPop: _onWillPop,
      child: Scaffold(
          appBar: AppBar(
            title: Text('KeyListener Demo'),
          ),
          body: Center(
            child: Text("按键监听"),
          )),
    );
  }
  
  // 返回键拦截执行方法
  Future<bool> _onWillPop() {
    int now = DateTime.now().millisecondsSinceEpoch;
    print(now - last);
    if (now - last > 1000) {
      last = now;
      // showToast("再按一次返回键退出");
      return Future.value(false); //不退出
    } else {
      return Future.value(true); //退出
    }
  }
}

26、Flutter如何JSON编解码?

我们这里只讲解最基础的编解码,复杂的需要创建Model的建议借助三方库。如Flutter官方提供了一个插件库:json_serializable。

当我们去请求网络数据接口或者缓存某些结构数据时,一般都会用到JSON数据交换格式。JSON在移动端、后端、前端中应用都非常广泛。在Flutter中JSON格式的解析使用 'dart:convert’里的函数类进行编解码处理。
我们看一个最简单的编解码使用的例子:

// JSON解码
// 定义一个JSON格式字符串
String _jsonString = '{"name": "Flutter Book","author": "Google"}';

// 使用json.decode进行解码
Map<String, dynamic> book = json.decode(_jsonString);

// 解码后调用获取值
Column(
    children: <Widget>[
        Text('Book Name:${book['name']}'),
        Text('Book Author:${book['author']}'),
    ],
));

// 再看下JSON编码
// 使用json.encode将实体对象编码为JSON字符串
String _bookJson = json.encode(book);

怎么样,用起来是不是很简单,这些只是最简单的例子。实际开发中可能会遇到更加庞大、复杂嵌套的JSON结构。

// 如果是一个List集合的JSON字符串的话
String _jsonListString =
      '[{"name": "Flutter Book","author": "Google"},{"name": "Dart Book","author": "Google"}]';

// 解码成List
List books = json.decode(_jsonListString);

// 调用取值
print(books[0]["name"]);

27、Flutter Hero动画是做什么的?

Flutter Hero动画是专门用来做页面跳转效果的,例如一个页面跳转到另一个页面,可以使用一些过渡动画和效果。

Hero动画主要用于页面跳转切换时的某个Widget的过渡跳转动画效果,也叫共享元素过渡动画。用户从页面中选择一个元素(通常是一个图像),然后打开所选元素的详情页面。这个过程中元素和页面执行的动画就是Hero共享元素过渡动画。

例如我的一个页面有一个头像,点击头像跳到另一个页面,头像有一个动画,新页面打开也有一个过渡动画。

我们先看下Hero动画的基本使用方式:

  • 先要在页面A和页面B分别定义一个Hero Widget,并且设置相同的tag值,这样才可以匹配;
  • 路由里配置从页面A跳转到页面B;
  • 点击跳转执行动画。

Hero 动画执行过程: Flutter框架会根据这两个Hero Widget计算出一个补间矩形 ,将这个补间矩形作为一个中间的遮罩层作为动画过渡。在跳转过程中,页面A的Hero Widget会跳转到中间遮罩层,然后进入到页面B。

28、Flutter 可以开发TV应用吗?

勉强可以,不过非常麻烦,开发效率低,焦点处理非常不方便。并且开发出来的应用性能很差,在某些机顶盒上非常卡顿,消耗很大资源。建议使用原生Anroid进行机顶盒开发,有Google Android TV官方支持,也有相应的库支持。

29、Flutter 根目录的main.dart可以改名或者移动到其他目录吗?

不可以,这个是 Flutter 入口文件类,固定的名称和内部逻辑。

30、Flutter 如何实现复制到剪贴板?

  ///复制到剪贴板
  void setClipData(String text) {
    Clipboard.setData(ClipboardData(text: text));
  }

31、Flutter 和Dart 目前还可以做哪些方面的开发?

Flutter目前是移动Android和IOS端的应用研发,基础功能都可以实现,一些需要原生支持的可以使用插件。实在插件都没有的话,需要自己的编写插件库进行实现原生的功能。

Flutter 的 Web 开发目前已经有官方示例了:flutter.github.io/samples

正式版本Flutter Web SDK应该很快了。

Dart 的话目前可以开发后端服务器了,可以写接口及相关的后端和服务器逻辑,已经测试。

Dart 目前还可以替代 JS 编写 Dart 版本的 JS 逻辑。

32、Flutter 时间类如何使用?

使用DateTime这个类。

var now = DateTime.now();
  String time = now.year.toString() +
      "-" +
      now.month.toString() +
      "-" +
      now.day.toString() +
      "  " +
      now.hour.toString() +
      ":" +
      now.minute.toString() +
      ":" +
      now.second.toString();

还有很多方法,具体用法大家可以进行API调用查看,非常简单。

33、Flutter 如何设置状态栏颜色和图标颜色?

可以通过 AppBar 的 brightness 或者 ThemeData 去设置状态栏颜色。

但是如果不想用 AppBar ,那么我们还可以嵌套 AnnotatedRegion 去设置状态栏样式,通过 SystemUiOverlayStyle 就可以快速设置状态栏和底部导航栏的样式。

同时还可以通过 SystemChrome.setSystemUIOverlayStyle 去设置,前提是没有使用 AppBar 。需要注意的是,所有状态栏设置是全局的,如果在 A 页面设置后,B 页面没有手动设置或者使用 AppBar ,那么这个设置将直接呈现在 B 页面。

34、如何查看某个应用或某个页面是否是使用 Flutter 写的?

目前Android 平台的应用通过开启开发者模式里的:开发者选项 -> 显示布局边界 分辨出来。原生应用的每个控件都会用边框分割出来,而 Flutter 的应用页面无边界分割,是个整体的SurfaceView。

Flutter 和 Dart 的未来展望

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XwiWWhyP-1569849191793)(images/flutter_all/reflectly-hero-600px.png)]
Google公司于2018年12月5日发布了Flutter 1.0正式版,大半年的时间过去了,Flutter最新版本已经到了V1.8.4了,更加的完善和稳定。Dart的最新版本已经到了v2.5.0版本。Flutter和Dart的更新频率很快,并且官方维护的一些插件库和开发者提交的插件库已经越来越多,相关的文档、资源也越来越多,Flutter生态也已经逐步完善。

Flutter目前发展

Flutter从1.0正式版发布的大半年的时间里,开发者数量、插件库数量等都在指数级增长,吸引了来自全球各个国家的开发者和科技公司,目前Flutter已经成为最热门的开源项目之一了。当然,Flutter在国内的发展也非常的迅猛。在 StackOverflow 2019 年的全球开发者问卷调查中,Flutter 被选为最受开发者欢迎的框架之一,超过了 TensorFlow 和 Node.js。
在这里插入图片描述
全球已经有很多大家熟悉的公司采用了 Flutter进行研发,包括很多国内的知名公司。比如阿里巴巴、腾讯、京东、美团等。
在这里插入图片描述
来自丹麦的 Reflectly应用,已经率先采用了Flutter进行重写了客户端,一套Flutter代码编写了Android和IOS端Reflectly应用。
在这里插入图片描述
在国内,Flutter的开发者和社区非常的活跃,其中最令人激动的就是:在今年Google I/O 前举办的全球 Flutter Create 大赛中,来自中国广东的胡泽标凭借一个特别精致的罗盘应用摘得了Flutter Create全球大奖。
在这里插入图片描述
获奖证书:
在这里插入图片描述
更多参赛作品及源码可以在:https://flutter.dev/create 这里进行查看和学习。

前面我们介绍过,Flutter会支持大部分的主流平台,一套语言、一套逻辑就可以实现跨多平台。如:Android、IOS、Web、PC、Fuchsia OS、物联网等主流平台。
在这里插入图片描述

Fuchsia OS

除了Flutter外,Google的Fuchsia OS也已经成为了一个未来的热门操作系统,虽然还没有推出正式版本,但是它的目标和特点已经吸引了一大批开发者和学习爱好者。Flutter和Dart开发的应用是Fuchsia OS默认支持的。早在2016年,Google秘密研发Fuchsia操作系统的就被首次曝光。Fuchsia OS是一套可运行在手机、平板、PC等平台的跨平台系统,放弃Linux内核,而是基于Zircon微核,采用Flutter引擎+Dart语言编写。预测可能在2020~2021年Fuchsia OS正式版将会推出使用,或许会替代Android系统。据传,Google已经聘请了有着10多年Mac OS开发经验的资深苹果系统开发工程师Bill Stevenson来操盘Fuchsia,目标是推向成熟市场。华为的很多设备也已经很早就配合Flutter和Fuchsia OS进行了测试。我们也期待Fuchsia OS可以早日推出。

Flutter Web

在这里插入图片描述
除了Flutter移动平台外,可能最引入瞩目的就是Flutter Web(https://flutter.dev/web) 的支持了,虽然Web SDK正式版还没有发布,不过通过预览测试版我们就可以有理由相信Flutter Web将会大大简化我们开发Web页面的成本。无需编写繁杂的CSS和JS、HTML,一套Flutter代码就轻松搞定一个Web页面系统。

目前,Flutter for Web 的示例应用在桌面浏览器基本能达到每秒 60 帧的渲染速度。但是在移动浏览器,特别是在低端机型上还有很大的优化空间。

Flutter Web官方的测试预览例子可以在:flutter.github.io/samples 进行学习和体验,目前通过这几个例子来看,效果非常的不错。
在这里插入图片描述
Flutter for Web 目前处在技术预览阶段,相信很快会推出正式版本。

Flutter 桌面和嵌入式

Flutter 也将支持桌面PC平台。目前处于研发实验阶段。未来可以用Flutter开发Mac、Windows 和 Linux 、Chrome OS 、Fuchsia OS上运行的 Flutter 应用。

Flutter桌面的实验性项目:https://github.com/google/flutter-desktop-embedding
Flutter 桌面的早期说明:https://github.com/flutter/flutter/wiki/Desktop-shells

Flutter未来也将支持在嵌入式设备商进行开发和运行,例如在Raspberry Pi 等小型设备上运行 Flutter 应用。

Flutter嵌入式示例:https://medium.com/flutter-io/flutter-on-raspberry-pi-mostly-from-scratch-2824c5e7dcb1

Flutter嵌入式 API:https://github.com/flutter/flutter/wiki/Custom-Flutter-Engine-Embedders

目前项目处于实验测试阶段。

Flutter 游戏

在 Google I/O’19 期间,Flutter 团队和 2Dimensions 联合发布了一款运营 / RPG 游戏: Flutter Developer Quest。除了作为游戏本身在游戏性上毫不缩水外,代码也完全开源。这是一项Flutter在游戏开发上的新的尝试和应用拓展。

游戏源代码地址:https://github.com/2d-inc/developer_quest

Flutter Developer Quest,是一款完全由 Flutter 开发构建的游戏,游戏已经在 App Store 和 Google Play 上进行免费下载体验。Flutter Developer Quest 是一款基于屏幕进行交互的 RPG 类游戏,游戏展示了许多最新的 Flutter 功能。

这是一个新的拓展和尝试,大家可以自行进行游戏源码的阅读和学习。

Flutter 近期展望

Flutter的近期动态已经在FlutterGithub主页的Github wiki 上进行了公开。当然我们也可以关注:谷歌开发者这个微信公众账号获取更多更新的动态消息。
在这里插入图片描述
地址:https://github.com/flutter/flutter/wiki/Roadmap ;
https://github.com/dart-lang/language

“accepted” 目录中的为工程实施阶段,“working” 目录中的为设计阶段,大家可以持续关注。
Flutter和Dart都在按照计划进行加紧研发中。相信不久我们便可以看到一些关于Flutter和Dart的新的东西。

总结

关于 Flutter 和 Dart 开发相关的分享就暂时这么多,后续遇到了相关问题继续更新和分享。也欢迎广大读者反馈问题及解决方案,一起进步、一起学习、一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章