大佬们点波关注呀,从小家境贫寒,没见过四位数字,让我关注人数破千吧~

1.毁掉和未创建调用

及时停止或许毁掉监听,例如一个定时器:

Timer _countdownTimer;
  @override
  void dispose() {
    _countdownTimer?.cancel();
    _countdownTimer = null;
    super.dispose();
  }

为了稳妥我们还要在调用setState()前判别其时页面是否存在:

_countdownTimer = Timer.periodic(Duration(seconds: 2), (timer) {
    if (mounted){
      setState(() {
      });
    }
  });

2.先制造再恳求

addPostFrameCallback回调方法在Widget渲染完结时触发,所以一般我们在获取页面中的 Widget 巨细、方位时运用到。

处理方法就是运用addPostFrameCallback回调方法,等候页面 build 完结后在恳求数据:

@override
void initState() {
  WidgetsBinding.instance.addPostFrameCallback((_){
    /// 接口恳求
  });
}

3.坚持页面情况

比如点击导航栏来回切换页面,默许情况下会丢掉原页面情况,也就是每次切换都会从头初始化页面。这种情况处理方法就是PageViewBottomNavigationBar结合运用,一起子页面State中继承AutomaticKeepAliveClientMixin并重写wantKeepAlive为true。代码大致如下:

class _TestState extends State<Test> with AutomaticKeepAliveClientMixin{
  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Container();
  }
  @override
  bool get wantKeepAlive => true;
}

4.预先缓存图片

在 Flutter 中,加载本地图片会存在一个加载进程。比如点击图标做图标的切换时,那么初度会发生闪烁的情况。特别是做类似引导页这类需求是,通过左右滑动切换图片时会发生比较明显的白屏一闪而过。

Flutter Tips 小技巧(更新中)

处理方法很简略,就是运用 precacheImage,它将图像预存到图像缓存中。假如图像稍后被 Image、BoxDecation 或 FadeInImage 运用,它会被加载得更快。

precacheImage(AssetImage("assets/logo"), context);

本问题详细的代码见:点击查看。

5.屏幕方向

新建的 Flutter 项目默许并没有束缚屏幕的横竖屏,所以假如你的项目并没有适配横竖屏,需求束缚某一方向。我以束缚竖屏为例:

void main(){
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown
  ]).then((_){
    runApp(MyApp());
  });
}

6.FutureBuilder 懒加载

Flutter 中通过 FutureBuilder 或许 StreamBuilder 可以和简略的完结懒加载,通过 future 或许 stream “异步” 获取数据,之后通过 AsyncSnapshot 的 data 再去加载数据,至于流和异步的概念,以后再打开吧。

const FutureBuilder({
    Key key,
    this.future,//获取数据的方法
    this.initialData,
    @required this.builder//依据快照的情况,回来不同的widget
  }) : assert(builder != null),
       super(key: key);

future 就是一个定义的异步操作,留意要带上泛型,不然后边拿去 snapshot.data 的时分效果是 dynamic 的 snapshot 就是 future 这个异步操作的情况快照,依据它的 connectionState 去加载不同的 widget 有四种快照的情况:

enum ConnectionState {
   //future还未实行的快照情况
  none,
  //连接到一个异步操作,而且等候交互,一般在这种情况的时分,我们可以加载个菊花
  waiting,
  //连接到一个活泼的操作,比如stream流,会不断地回来值,并还没有结束,一般也是可以加载个菊花
  active,
  //异步操作实行结束,一般在这儿可以去拿取异步操作实行的效果,并显示相应的布局
  done,
}

7.StreamBuilder 流控制管理

从一端发射一个作业,从别的一端去监听作业的改变,通过 Stream 我们可以在 Flutter 上规划出依据作业流驱动的响应式代码逻辑。常用于会屡次读取数据的异步任务场景,如网络内容下载、文件读写等。

class StreamDemo extends StatefulWidget {
  @override
  _StreamDemoState createState() => _StreamDemoState();
}
class _StreamDemoState extends State<StreamDemo> {
  var index = 0;
  var streamController;
  StreamSink<String> get streamSink => streamController.sink;
  Stream<String> get streamData => streamController.stream;
  @override
  void initState() {
    super.initState();
    streamController = StreamController<String>();
    streamSink.add("0");
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text('streamBuilder')),
        body: StreamBuilder<String>(
          stream: streamData,
          builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
            return Text('Result: ${snapshot.data}');
          },
        ),
        floatingActionButton: FloatingActionButton(
            onPressed: onFloatActionButtonPress, child: Icon(Icons.add)));
  }
  void onFloatActionButtonPress() {
    index++;
    streamSink.add(index.toString());
  }
}

8.Future 建议多个异步操作的方法

复杂耗时操作或许多个网络恳求并对完好性要求较高的操作,可以运用 Future 异步方法:

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);
});

9.Text 上下边距调整

Flutter Tips 小技巧(更新中)

左边的五颜六色矩形是支柱(虽然实际上支柱没有宽度)。这个矩形的高度是最小的线条高度。这条线不能再短了。但它可以更高。

  • 上升是从基线到文本顶部的距离(由字体定义,而不是任何特定的字形)
  • 下降是从基线到文本底部的距离(由字体定义,而不是任何特定的字形)

前导(读作“ledding”,如旧排字机用来排字的铅字金属)是一行底端和下一行顶端之间的距离。在支柱中,一半的引线放在顶部,一半放在底部。是图中的灰色区域。 > 可以运用乘数更改支柱的垂直尺度。

class TextLineHeight extends StatelessWidget {
  final String textContent;
  final double leading;
  final double textLineHeight;
  final double fontSize;
  TextLineHeight({
    this.textContent,
    this.leading: 0,
    this.textLineHeight: 0.93,
    this.fontSize: 26,
  });
  @override
  Widget build(BuildContext context) {
    return Transform.translate(
      offset: Offset(0, fontSize/11.52),
      child: Text(
        textContent,
        strutStyle: StrutStyle(
          forceStrutHeight: true,
          height: textLineHeight,
          leading: leading,
          fontSize: fontSize,
        ),
        style: TextStyle(
          fontSize: fontSize,
          color: Colors.black,
        ),
      ),
    );
  }
}

效果比照:

Flutter Tips 小技巧(更新中)

10.空判别

运用null-aware operators判别 null,减少代码量。

// User below
title ??= "Title";
// instead of
if (title == null) {
  title = "Title";
}

上面的方法可以在只需一层判别中做保护,假如你有一连串的’.’取值,那就需求用这种方法了。

xxCount = xxModel?.xxInfo?.xxCount ?? 0

11.VSCode 配备项

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Main",
            "type": "dart",
            "request": "launch",
            "program": "lib/main.dart”,
            "flutterMode": "profile" # 测验完后记得把它改回去!
        },
        {
            "name": "Dev",
            "type": "dart",
            "request": "launch",
            "program": "lib/main_dev.dart",
            "args": [
                "--flavor",
                "universal",
            ],
            "flutterMode": "profile" # 测验完后记得把它改回去!
        },
        {
            "name": "Prod",
            "type": "dart",
            "request": "launch",
            "program": "lib/main_prod.dart",
            "args": [
                "--flavor",
                "universal",
            ],
        },
    ]
}

12.运用私家库 pubspec.yaml

可以指定 Git 私家库,引证链接、分支、tag,运用示例:

dependencies:
  # 混编路由库
  flutter_boost:
    git:
      url: git@github.com:ReverseScale/flutter_boost.git
      ref: v1.1

官网:dart.dev/tools/pub/d…

13.查看是否为发布环境

判别其时环境是否为发布方式:

const bool kReleaseMode = bool.fromEnvironment('dart.vm.product')

运用体系级顶级情况 kReleaseMode 获取其时是否为发布环境:

import 'package:flutter/foundation.dart';
print('Is Release Mode: $kReleaseMode');

运用这个可以用于控制日志输出,比如 release 方式关闭日志:

if (isProduction) {
  debugPrint = (String message, {int wrapWidth}) => {};
}

14.情况栏的高度

final double statusBarHeight = MediaQuery.of(context).padding.top;

15.底部栏的高度

final double bottomHeight = MediaQuery.of(context).padding.bottom;

16.科学计数法表达

// 输出:1e-8
var number = 1 / pow(10, 8);
// 输出:0.00000001
print(aaaa.toStringAsFixed(8));

17.InheritedWidget 存值方法

1.声明 ShareDataWidget:

class ShareDataWidget extends InheritedWidget {
  int count;
  ShareDataWidget({
    @required this.count,
    Widget child
  }): super(child: child);
  static ShareDataWidget of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(ShareDataWidget);
  }
  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return true;
  }
}

2.存入传递值,例如:body 下嵌套传值:

body: ShareDataWidget(
          count: this.widget.count,
          child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: <Widget>[
                  Text("Widget数量: ${this.widget.count}"),
                  _Counter(),
                  RaisedButton(
                    child: Text("添加"),
                    onPressed: () => setState(() => widget.count ++),
                  )
              ]
            ),
          ),
        )

3.取出传递值:

ShareDataWidget sdw = context.inheritFromWidgetOfExactType(ShareDataWidget);
print(sdw.count);

18.Cocoapod 引入 Flutter Framework

flutter_application_path = '../Seller/'load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')install_all_flutter_pods(flutter_application_path)

19.mixins 语法(高级)

class Musician extends Performer with Musical {
  // 
}
class Maestro extends Person
    with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}
mixin Musical {
  bool canPlayPiano = false;
  bool canCompose = false;
  bool canConduct = false;
  void entertainMe() {
    if (canPlayPiano) {
      print('Playing piano');
    } else if (canConduct) {
      print('Waving hands');
    } else {
      print('Humming to self');
    }
  }
}

20.泛型示例

var names = <String>['Seth', 'Kathy', 'Lars'];
var pages = <String, String>{
  'index.html': 'Homepage',
  'robots.txt': 'Hints for web robots',
  'humans.txt': 'We are people, not machines'
};
To specify one or more types when using a constructor, put the types in angle brackets (<...>) just after the class name. For example:
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
var nameSet = Set<String>.from(names);
var views = Map<int, View>();

打印泛型类型

class Foo<T extends SomeBaseClass> {
  // Implementation goes here...
  String toString() => "Instance of 'Foo<$T>'";
}
class Extender extends SomeBaseClass {...}

21.导入库

库冲突

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

导入库的一部分

// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;

懒加载库

import 'package:greetings/hello.dart' deferred as hello;
Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

22.Generators(迭代生成器)

同步的Generators

Iterable<int> naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}

异步的Generators

Stream<int> asynchronousNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

23.定义元数据

示例

library todo;
class Todo {
  final String who;
  final String what;
  const Todo(this.who, this.what);
}
运用
import 'todo.dart';
@Todo('seth', 'make this do something')
void doSomething() {
  print('do something');
}

24.私有 Pub

地址:pub.dev/packages/pu…

~ $ git clone <https://github.com/dart-lang/pub_server.git>
~ $ cd pub_server
~/pub_server $ pub get
...
~/pub_server $ dart example/example.dart -d /tmp/package-db

参数释义:

-s 是否fetch官方库房
-h ${ip / domain}
-p 端口
-d 上传上来的插件包在服务器上的存储地址

运用它可以将新软件包上载到本地作业的服务器或下载本地可用的软件包或通过回退到以下软件包 pub.dartlang.org 很容易:

~/foobar $ export PUB_HOSTED_URL=http://localhost:8080
~/foobar $ pub get
...
~/foobar $ pub publish
Publishing x 0.1.0 to <http://localhost:8080>:
|-- ...
'-- pubspec.yaml
Looks great! Are you ready to upload your package (y/n)? y
Uploading...
Successfully uploaded package.

完善 pubspec.yaml 文件

插件称号
description: 插件描绘
version: 0.0.1 版本号
author: xxxx<xx@xxx.com>
homepage: 项目主页地址
publish_to: 填写私有服务器的地址(假如是发布到flutter pub则不用填写,插件默许是上传到flutter pub)

发布至私有服务器

flutter packages pub publish --server $服务器地址

25.原生和 Flutter 之间数据交互类型有束缚

在进行插件的开发时,就必定会涉及到原生和 Flutter 之间的数据交互.这儿需求留意的是,就像我们在进行 ReactNative 和 JNI 的开发时,并不是什么类型的数据都是支持交互的。下面我给出原生和 Flutter 之间可交互的数据类型:

数据类型比照

这儿我们用得最多的就是 boolintStringMap 这几个类型了。

26.检测方式

原来 flutter 现已帮我们封装好了检测方式的方法了:

import 'package:flutter/foundation.dart';
if(kReleaseMode){
    // release 方式该做的事
}
if(kProfileMode){
    // profile 方式该做的事
}
if(kDebugMode){
    // debug 方式该做的作业
}

看下源码:

// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// A constant that is true if the application was compiled in release mode.
///
/// More specifically, this is a constant that is true if the application was
/// compiled in Dart with the '-Ddart.vm.product=true' flag.
///
/// Since this is a const value, it can be used to indicate to the compiler that
/// a particular block of code will not be executed in release mode, and hence
/// can be removed.
const bool kReleaseMode = bool.fromEnvironment('dart.vm.product', defaultValue: false);
/// A constant that is true if the application was compiled in profile mode.
///
/// More specifically, this is a constant that is true if the application was
/// compiled in Dart with the '-Ddart.vm.profile=true' flag.
///
/// Since this is a const value, it can be used to indicate to the compiler that
/// a particular block of code will not be executed in profle mode, an hence
/// can be removed.
const bool kProfileMode = bool.fromEnvironment('dart.vm.profile', defaultValue: false);
/// A constant that is true if the application was compiled in debug mode.
///
/// More specifically, this is a constant that is true if the application was
/// not compiled with '-Ddart.vm.product=true' and '-Ddart.vm.profile=true'.
///
/// Since this is a const value, it can be used to indicate to the compiler that
/// a particular block of code will not be executed in debug mode, and hence
/// can be removed.
const bool kDebugMode = !kReleaseMode && !kProfileMode;

源码简简略单的定义了三个常量:kReleaseModekProfileModekDebugMode 别离对应着 release 方式、 profile 方式、debug 方式

原理也就是对 bool.fromEnvironment() 的封装,但是少些的东西,而且还给了默许值,用它没错了。

27.断言 assert 用途

assert 只会在 debug 方式才会作业,不只可以用来判别作业方式,还可以用来查看程序是否按要求作业。

比如:一个函数传入的参数不能为 null:

void test(String name){
  assert((){
        print('用户称号为空');
        name!=null
    }());
  // 其他作业
}

上面的比如是 name 是从后台获取的用户称号,但是假如没获取到 或许后台报错,那么就可以很快定位错误了。

28.数据传递的三种计划

  1. InheritedWidget: 适用于父组件传递给子组件的场景, 可跨层级。
  2. Notification:适用于子组件告知父组件数据改动的场景。
  3. Event Bus:作业广播,可适于于各种类型的数据告知同步。

29.直播制造瓶颈问题

Flutter定义的 Channel 机制从本质上说是供应了ー个消息传送机制,用于图像等数据的传输,这必定会引起内存和 CPU 的巨大消耗。

Flutter Tips 小技巧(更新中)

处理:外接纹理 + PixelBuffer 通道传输 OpenGLES Texture 给 Skia 直接制造。

  • **节约 CPU 时间。**从我们的测验效果来看,Android 机型上ー帧 720P 的 RGBA 格式的视频,从 GPU 读取到 CPU 需求 5ms 左右,从 CPU 再传送到 GPU 又需求 5ms 左右,即便引入了 PBO,也还有 5ms 左右的耗时,这关于高帧率场景显然是不能接受的。
  • **节约 CPU 内存。**数据都在 GPU 中传递,关于图片场景特别适用,由于或许同一时间会有许多图片需求展现。

30.Sentry 失常收集

Flutter 引擎现已默许帮忙我们收集了未处理失常并供应了共同处理这些失常的接口,通过给 FlutterError.onError 注册回调,这些失常就可以被手动处理,再协作 Sentry 即可完结自动地收集失常及其详细的库房信息。

添加 Sentry 的Dart 版客户端并运用 DSN 进行初始化,DSN 可以在 Sentry 面板中找到。

import 'package:sentry/sentry.dart';
final sentry = SentryClient(dsn: 'DSN');

引入flutter/fondation并注册回调,这些失常信息将通过 Sentry 客户端进行上传。

import 'package:flutter/foundation.dart';
...
FlutterError.onError = (e) =>
        sentry.captureException(exception: e.exception, stackTrace: e.stack);
...

但是在开发环境下,我们一般需求直接在控制台内输出失常信息以帮忙开发者快速修复问题,而不期望将它们反馈至 Sentry 。通过 DartVM 的环境变量dart.vm.product, 我们可以判别其时是否处于生产方式,并仅在生产方式下注册失常收集回调。

...
if (bool.fromEnvironment('dart.vm.product'))
    FlutterError.onError = (e) =>
        sentry.captureException(exception: e.exception, stackTrace: e.stack);
...

31.Dispose 方法留意

重写 dispose 方法并配备 AnimationController 实例。

@override
dispose() {
  animationController.dispose(); 
  super.dispose();
}

32.获取 Render 方法

创建 GlobalKey

GlobalKey globalKey = GlobalKey();

对应的Widget 引证 ,如这儿的Text

Text('张三',key:globalKey);

通过 globalKey 来获取 对应的Element(BuildContext)

BuildContext stackContext = globalKey.currentContext;

获取对应的 RenderObject

RenderBox renderBox = stackContext.findRenderObject();
/// 相关于全局的方位
Offset offset = renderBox.localToGlobal(Offset.zero);
/// 获取指定的Widget的巨细 信息
Size size = renderBox.paintBounds.size;