大前端开发

Flutter开发中遇到的问题和解决方法(持续更新)

2022-08-01  本文已影响0人  elite_kai
1.Flutter使用InkWell无水波纹效果
2.Flutter 分类网站推荐
3.tabbar设置点击的背景颜色
Container(
            color: Colors.white,
            child: Stack(
              children: [
                Positioned(
                  left: MediaQuery.of(context).size.width / 3,
                  child: Container(
                    width: 2,
                    height: 42,
                    color: Colors.grey[200],
                  ),
                ),
                Positioned(
                  left: MediaQuery.of(context).size.width / 3 * 2,
                  child: Container(
                    width: 2,
                    height: 42,
                    color: Colors.grey[200],
                  ),
                ),
                TabBar(
                  labelStyle:
                      TextStyle(color: Colors.white, fontSize: AppFont.f14),
                  unselectedLabelColor: Colors.black,
                  unselectedLabelStyle:
                      TextStyle(color: Colors.grey, fontSize: AppFont.f14),
                  indicator: BoxDecoration(
                      color: Global()
                          .appConfig
                          .colorTheme
                          .pressStateStartButtonBackground),
                  controller: viewModel.tabController,
                  tabs: viewModel.items
                      .map((e) => e.itemBuilder(context))
                      .toList(),
                ),
              ],
            ),
          ),
4.Flutter Column嵌套 Row Text 超出屏幕
https://www.codeleading.com/article/24305929369/
说明:当这个Text超出屏幕时,不只是他自己控件上显示黄色警告,在它父布局上也有,说明父布局超出了,就应该修改父布局的显示问题
5.了解Flutter Sliver 组件
6.Dart判断数组是否全选方法
/// 是否全选
bool get isAllSelected => fields.every((field) => field.isSelected);
7.设置项目全局context

在根页面中设置全局的context
获取全局的 globalKey.currentState!.context
参考toast的使用 https://github.com/ponnamkarthik/FlutterToast

import 'package:flutter/material.dart';

GlobalKey globalKey = GlobalKey();

void main() => runApp(
      MaterialApp(
        home: MyApp(),
      ),
    );

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: globalKey,

    );
  }
}
8.解决 Flutter showModalBottomSheet 被弹出的键盘、输入法遮挡
在使用 showModalBottomSheet 底部弹出内容时,
如果唤起了键盘、输入法,showModalBottomSheet 中的内容往往会被挡住,
给 showModalBottomSheet 设置 isScrollControlled
再套一个 SingleChildScrollView 以及 设定一个 padding 一般就能解决问题了。
        showModalBottomSheet(
          context: context,
          isScrollControlled: true,
          builder: (context) {
            return SingleChildScrollView(
              child: Container(
                padding: EdgeInsets.only(
                  bottom: MediaQuery.of(context).viewInsets.bottom,
                ),
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    OrderAllocationBottomView(
                      model: OrderAllocationModel(
                        color: 'A02',
                        styleList: [],
                        styleName: '服装服饰',
                        userName: '华中区',
                      ),
                    ),
                  ],
                ),
              ),
            );
          },
        );
9.碰到点击顶部区域无法响应点击事件
原因在于点击区域在安全区域状态栏里面 
所以点击没有反应,在安全区域进行点击即可
10.Flutter的Flexible和 Expanded的区别
 Flexible与Expanded的相同点是都必须使用在Row、Column、Flex内部
  https://www.jianshu.com/p/957f868e45ab
11.对于StatefulWidget怎么更新数据源刷新页面
第一种通过 ChangeNotifier 传入一个Controller 控制
第二种通过 didUpdateWidget 对比数据源进行更新 第二种比较常用 比较推荐
12.Flutter动态的获取widget宽高
获取某个控件的宽高,可以通过设置GlobalKey来实现。
final GlobalKey globalKey = GlobalKey();
Container(
   key: globalKey,
)
注意,需要等widget布局完成之后才能获取宽高
var width = globalKey.currentContext?.size?.width ?? 0;
var height = globalKey.currentContext?.size?.height ?? 0;
13.hitTest:自绘组件点击事件的处理
自绘组件点击事件的处理(尤其是外层使用Stack时更需要注意)

首先需要重写hitTest()函数
默认情况下返回null,事件不会向下传递,也不会进行处理
如果返回true则当前组件进行处理事件
如果返回false则当前组件不会响应点击事件,会向下一层传递

@override
bool? hitTest(Offset offset) => null;

如果需要处理点击事件,也可通过此方法进行处理,并可通过offset确认点击的位置,比如:

@override
bool? hitTest(Offset offset) {
    for (int i = 0; i < paths.length; i++) {
      if (paths[i].contains(offset) && !holePath.contains(offset)&&tapIndex!=i) {
        onTap?.call(i);
        tapIndex = i;
        return true;
      }
    }
    return false;
}
14.计算文本的宽高
计算文本的宽高可以通过TextPainter来计算

extension TextSizeExtension on Text {
  Size calculateTextSize(
    BuildContext context,
  ) {
    if (data == null || (data?.isEmpty ?? true)) {
      return Size.zero;
    }
    TextPainter painter = TextPainter(
      locale: Localizations.localeOf(context),
      maxLines: maxLines,
      textDirection: TextDirection.ltr,
      text: TextSpan(
        text: data,
        style: style,
      ),
    );
    painter.layout();
    return painter.size;
  }
}

使用
var textSize = Text("text").calculateTextSize(context);
15.IgnorePointer组件
IgnorePointer可以拦截触摸事件,让其子Widget不再响应相关事件。  
比如,我需要禁止ListView滑动

IgnorePointer(
      child: ListView.builder(
        shrinkWrap: true,
        itemCount: value.length,
        itemBuilder: (context, index) {
          return Container();
        },
      ),
    )

原理
有一个默认参数ignoring为true,最终作用于hitTest中判断是否拦截此事件
image.png image.png
16.LayoutBuilder组件:在build的时候获取当前Widget的大小
如果需要根据当前组件的大小进行布局便可使用此组件,constraints.maxHeight为组件的高度,constraints.maxWidth为组件的宽度

@override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (BuildContext context, BoxConstraints constraints) {
        var color = Colors.red;
        if (constraints.maxHeight > 100) {
          color = Colors.blue;
        }
        return Container(
          height: 50,
          width: 50,
          color: color,
        );
      },
    );
  }
17.Dart 数组reduce

Dart数组的reduce可用指定的函数方式对数组中的所有元素做连续操作,并将结果返回,比如可以用于计算数组中的最大值,最小值,所有元素的和。

例:
1.数组中的最大值:
import 'dart:math';
var maxValue = lists.reduce(max)
/// 或者
var maxValue = values.reduce((v, e) => max(v, e))

2.数组中的最小值:
import 'dart:math';

var minValue = lists.reduce(min)
/// 或者
var minValue = values.reduce((v, e) => min(v, e))

3.数组中的所有元素的和
var sum = lists.reduce((v, e) => v + e)
18.Android 运行报错:App requires Multidex support
D8: Cannot fit requested classes in a single dex file (# methods: 67509 > 65536)
com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives: 
The number of method references in a .dex file cannot exceed 64K.
Learn how to resolve this issue at https://developer.android.com/tools/building/multidex.html
    ......

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:mergeDexDebug'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
   > com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives: 
     The number of method references in a .dex file cannot exceed 64K.
     Learn how to resolve this issue at https://developer.android.com/tools/building/multidex.html

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 52s
[!] App requires Multidex support

┌─ Flutter Fix ──────────────────────────────────────────────────────────────────────────────────┐
│ Flutter multidex handling is disabled. If you wish to let the tool configure multidex, use the │
│ --mutidex flag.                                                                                │
└────────────────────────────────────────────────────────────────────────────────────────────────┘
Exception: Gradle task assembleDebug failed with exit code 1


解决
修改模块级 build.gradle 文件以启用 MultiDex,并将 MultiDex 库添加为依赖项,如下所示
android {
    defaultConfig {
        ...
        multiDexEnabled true
    }
    ...
}

dependencies {
    implementation "androidx.multidex:multidex:2.0.1"
}
19.当TextField被Container包裹时如何使输入的文字上下居中

https://blog.csdn.net/qq1377399077/article/details/108852342

一般该场景我们来给输入框设置一个背景色,
但是有时候会发现输入框光标及内容并不居中(其实另一种InputDecoration也可以设置输入框的背景)
20.网络请求动态显示tab标签,设置TabController

https://blog.csdn.net/wayne214/article/details/104002718

在创建tabbar和tabview后,进行网络请求后显示顶部tab标签,
设置TabController,并使class类实现SingleTickerProviderStateMixin,
但是会报错“A SingleTickerProviderStateMixin can only be used as a TickerProvider once”,
主要是因为多个地方调用setState请求重绘,
但是state使用的是SingleTickerProviderStateMixin ,
将其改成TickerProviderStateMixin即可
21.Flutter 底部弹窗showModalBottomSheet如何调整高度

https://blog.csdn.net/Dnnis/article/details/116798633

弹窗默认为屏幕的二分之一无法改变高度,
但是将isScrollControlled方法设置为true,
然后重新设置一下builder里面的布局高度就可解决
22.tabbar设置每个item的宽度需要设置isScrollable为true

https://api.flutter.dev/flutter/material/TabBar/isScrollable.html

isScrollable设置为true后,可以自定义设置每个item的宽度
23.类似原生的生命周期
24.如何居中SingleChildScrollView使背景拉伸以填充屏幕?

https://www.thinbug.com/q/56497666

尝试下面这种方法可以
return SingleChildScrollView(
  scrollDirection: Axis.horizontal,
     child: ConstrainedBox(
       //Use MediaQuery.of(context).size.height for max Height
       constraints: BoxConstraints(minHeight: MediaQuery.of(context).size.height),
       child: Center(
         child: //Widget,
      ),
     ),
   );
25.怎么将&#128064;转换为表情?
网上找了很多文章,都没有关于这方面的说法。
今天无意间看到官方初始化String的方法,测试竟然成功了
,目前没保证这个方法覆盖所有的表情,理论上是可以的。

直接用谷歌搜索&#128064;,会直接变成表情。导致搜出出来,更换搜索关键词为&#128064
![image.png](https://img.haomeiwen.com/i2356427/22b2966ba7440bb6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

搜出来关键词Unicode Decimal Code,但是这个关键词也是没找到原因。
下面发现去掉&#;去掉只用中间的数字解决。

/// 解析消息内容的表情
  /// [content] 消息内容
  static String parseEmoji(String content) {
    /// 正则
    final reg = RegExp(r'&#[0-9]*;');
    final matchs = reg.allMatches(content);
    if (matchs.isEmpty) {
      return content;
    }
    for (var match in matchs) {
      final emojiCodeContent = match.group(0);
      if (emojiCodeContent == null) continue;
      final emojiCode =
          emojiCodeContent.replaceAll('&#', '').replaceAll(';', '');

      final emoji = String.fromCharCodes([int.parse(emojiCode)]);
      content = content.replaceAll(emojiCodeContent, emoji);
    }
    return content;
  }
26.怎么 ListView 更新数据之后自动滑动到底部
_imController.scrollController.animateTo(
  _imController.scrollController.position.maxScrollExtent,
  duration: const Duration(milliseconds: 300),
  curve: Curves.easeOut,
);

延时16毫秒之后 更新UI 因为60帧渲染师是16毫秒 但是卡顿就不好说了 
最好是200-500毫秒
监听渲染完毕 注意下面的代码不能写在initState里面,
请写在build方法里面,保证每次更新都重新注册。
  WidgetsBinding.instance?.addPostFrameCallback((_) {
    /// do something
  });


监听每一帧的回掉
    WidgetsBinding.instance?.addPersistentFrameCallback((timeStamp) {
      /// 监听是否需要滚动到底部
      if (_imController.isScrollToBottom) {
        _imController.isScrollToBottom = false;
        _imController.scrollController.animateTo(
          _imController.scrollController.position.maxScrollExtent,
          duration: const Duration(milliseconds: 300),
          curve: Curves.easeOut,
        );
      }
    });
27.flutter APP中禁止软键盘弹出,监听硬键盘输入方法

https://chowdera.com/2022/02/202202260549433593.html

禁止软键盘弹出只需要设置showCursor为true, readOnly为true,
可以聚焦但是不会弹出键盘。如果想要弹出键盘,
目前能想到的方法是设置一个bool值给readOnly属性,通过刷新页面的方式修改

TextFormField(
     controller: _controller,
     showCursor: true, // 显示光标
     readOnly: true, // 设置只读,点击输入框时便不会弹出软键盘
     onEditingComplete: () {
         onOperateLot('Add');
    },
     validator: (String value) {
            if (value == null || value.isEmpty) {
                return '该项为必填项';
             }
             return null;
     },
 )
28.实现一个类像方法一样掉用
image.png
29.DefaultTextStyle 可以统一默认子元素的样式
DefaultTextStyle(
      style: const TextStyle(
        fontSize: 28,
        color: Colors.white,
        fontWeight: FontWeight.bold,
      ),
      child: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text("欢迎登录 App 周报管理系统"),
            _buildInputContent(context: context),
          ],
        ),
      ),
    );
30.Flutter TextField的onEditingComplete与onSubmitted的区别
onEditingComplete回调无参数

onSubmitted回调是当前输入内容

onEditingComplete与onSubmitted都在同一个方法内调用.先调用onEditingComplete再调用onSubmitted.

有onEditingComplete方法时,会忽略设置了的TextInputAction类型系统默认焦点操作.(释放焦点,跳转下一个/上一个焦点)

一般情况下,只获取输入结果的实现onSubmitted

若要输入完成后,不让系统自动控制焦点跳转,实现onEditingComplete

看下源码

31.Flutter 监听页面跳转实现类似android的onResume onPause,ios的viewWillDisappear viewDidDisappear

https://blog.csdn.net/qq_20352713/article/details/125187928

通过RouteAware监听路由事件,通过WidgetsBindingObserver监听app状态.二者结合即可
32.Flutter TabView设置indicator宽度

https://blog.csdn.net/xudailong_blog/article/details/98965866

正常情况下,我们可以设置Tabbar的indicatorSize来设置indicator的宽度,
但是默认每个tabbar会平分,造成indicator也仅仅是有一定的距离来达到缩短的效果,
这个时候就需要重写indicator继承Decoration来实现对indicator宽度的修改。
33.Dart 解包转换操作
1 使用系统方法
String? name = "king";
name.runes.map((e) => 1).map((e) => e + 10).map((e) => [2]).first; /// [2]

2 使用 darty_json_safe 库
Unwrap(name).map((e) => 1).map((e) => e + 10).map((e) => [2]).value;
34.怎么实现两个滚动试图联动
   _leftScrollController.addListener(() {
      final _left = _leftScrollController.offset;
      final _right = _rightScrollController.offset;
      /// 为了自动更新左侧 导致右侧继续更新
      if (_left == _right) return;
      _rightScrollController.jumpTo(_leftScrollController.offset);
    });
    _rightScrollController.addListener(() {
      final _left = _leftScrollController.offset;
      final _right = _rightScrollController.offset;
      /// 为了自动更新右侧 导致左侧继续更新
      if (_left == _right) return;
      _leftScrollController.jumpTo(_rightScrollController.offset);
    });
35.Flutter 基于Intl插件实现app国际化

https://blog.csdn.net/qq_20352713/article/details/125259871?spm=1001.2014.3001.5501

含插件使用.及app内切换语言
36.dart多线程(Isolate,compute,LoadBalancer的使用)
isolate

//使用isolate方法计算
  createIsolateCountEven(int num) async {
    //1.创建当前isolateA的接收端口
    var aReceived = ReceivePort();

    staticVar = 3;
    //2.创建新的isolateB,并且把当前A的端口带过去
    var newIsolate =
        await Isolate.spawn(createNewIsolateCountEven, aReceived.sendPort);
    aReceived.listen((message) {
      if (message is SendPort) {
        //5.拿到发送端口可以发送消息了
        message.send(num);
      } else {
        _count = message;
        setState(() {});
        //不需要时 释放isolate
        newIsolate.kill();
      }
    });
  }
  //运行在isolateB的方法
  static void createNewIsolateCountEven(SendPort aPort) {
    //3.创建isolateB的端口
    var bReceived = ReceivePort();
    //4.把isolateB的的发送端口带过去
    aPort.send(bReceived.sendPort);
    bReceived.listen((message) {
      //6.监听到消息可以做处理了
      //调用static方法或者top-level方法
      var res = topCountEven(message);
      aPort.send(res);
    });
  }
//compute
  Future<int> computeCountEven(int num) async {
    ///compute 使用的必须是顶层函数或者是static函数
    return await compute(topCountEven, num);
  }
//LoadBalancer
  //1.找个地方创建[LoadBalancer]
  Future<LoadBalancer> loadBalancer =
      LoadBalancer.create(2, IsolateRunner.spawn);
  Future<int> loadBalancerCountEven(int num) async {
    //2.获取loadBalancer
    var lb = await loadBalancer;
    //3.传入函数,参数
    //run的泛型为<函数的返回值类型,参数类型>
    var res = await lb.run<int, int>(topCountEven, num);
    return res;
  }

isolate手动控制
compute一次性使用
LoadBalancer相当于线程池

37.Flutter 取两位小数 不要四舍五入的方法

https://www.jianshu.com/p/adfabf566335

使用toStringAsFixed()方法
38.解决键盘弹起遮挡showDialog
showDialog(
        context: context,
        builder: (BuildContext context) {
          return Container(
            padding: EdgeInsets.only(
              bottom: MediaQuery.of(context).viewInsets.bottom,
            ),
            child: Container(),
          );
        });
39.Mac 必备开源 app 的集合
40.Flutter 设置屏幕旋转方向
开发过程中,移动设备为了避免屏幕自动旋转而带来一些不好的显示效果(未适配的前提下),通常我们会将设备的方向定死始终竖屏,就需要用到下面这个方法:

SystemChrome.setPreferredOrientations([]);

可以写在App入口main方法里面或者某个widget的initState里面,方法参数是DeviceOrientation数组类型,系统为我们提供了四种DeviceOrientation,

portraitUp:固定的常规的竖屏显示方式;

portraitDown:固定的反方向的竖屏显示方式(倒置手机,App头部在手机底部);

landscapeLeft:固定的横屏显示方式(App头部显示在手机左侧);

landscapeRight:固定的横屏显示方式(App头部显示在手机左侧);

传空数据/四个类型都传:则四个方向都可以旋转显示(前提是开启自动旋转)

一般来说将该方法设置在main方法里面使整个app起作用,但可能需要某个页面单独配置则在ininState方法里面传入你所需要的方向即可
41.SliverPersistentHeaderDelegate 中build 方法参数 BuildContext 无法获取外部页面的 Provider 对象
目前解决办法,使用 SliverPersistentHeaderDelegate 外部的 BuildContext 获取 Provider 对象

原因在于 context.select() 只允许在build方法内部或者在LayoutBuilder方法内部,在SliverPersistentHeaderDelegate的内部就报错了
42.dart导入内置包(使用内部math中取最大值时遇到的问题)
1.导入内置包
Dart语言内置了一些常用的包,这些内置包随着Dart sdk一起安装在本地。导入内置包使用 dart: 作为路径前缀。

例子

// 导入内置math包,使用dart:作为前缀。
// math包主要提供一些数学相关的函数,例如,正弦函数、求最大值函数等等
import 'dart:math';

void main() {
  // 调用math包中的max函数,求两个数中的最大值。
  var a = max(1,100);
  print(a);
}

2.包的别名

通过1的例子我们知道,默认情况调用包中的函数或者类,不需要包名作为前缀,上面调用了math包中的max函数,直接使用包中的函数名。但是这样会存在命名冲突的可能性,如果导入的两个包,包含了同名的类或者函数,就会出现命名冲突,因此提供别名机制。

别名例子:

// 使用 as 关键词,指定包的别名
import 'dart:math' as math;

void main() {
  // 使用别名,引用包中的函数 。
  var a = math.max(1,100);
  print(a);
}
提示: 推荐使用别名引用包的成员,不仅可以解决命名冲突问题,也可以增加代码的可读性,我们可以直观知道到底引用的是那个包的成员。

3.导入包的部分内容。

有时候我们不想导入整个包,只想导入包里面的某个类或者某个函数。dart提供了show和hide关键词处理导入包的部分内容。

例子:

// 仅导入max函数, 导入多个内容使用逗号分隔,例如 show max,sin
import 'dart:math' show max;

// 除了max函数,导入math中的所有内容。
import 'dart:math' hide max;
43.Flutter json_serializable库解析泛型最佳方案
将JsonSerializable的genericArgumentFactories参数设置为true
修改fromJson,toJson

@JsonSerializable(genericArgumentFactories: true)
class BaseJson<T> {
  int? code;
  String? msg;
  T? data;

  BaseJson();

  factory BaseJson.fromJson(
          Map<String, dynamic> json, T Function(dynamic json) fromJsonT) =>
      _$BaseJsonFromJson(json, fromJsonT);

  Map<String, dynamic> toJson(
    Object? Function(T value) toJsonT,
  ) =>
      _$BaseJsonToJson(this, toJsonT);
}

使用

 var jsonData =
        "{\"code\": 200001,\"msg\": \"success\",\"data\": {\"mydata\": {\"name\": \"张三\",\"age\": \"21\",\"tel\": \"1539324****\",\"balance\": \"100\"}}}";

//JSON字符串解析成map
    var jsonMap = json.decoder.convert(jsonData);

//map转model
    BaseJson<Data1<MyData>> baseJson = BaseJson.fromJson(jsonMap, (json) {
//多层泛型就这样多层解析
      return Data1.fromJson(json, (json) {
        return MyData.fromJson(json);
      });
    });

//model转jsonMap
    print(baseJson.toJson((value) => value.toJson((value) => value.toJson())));


data为List时
    var jsonData =
        "{\"code\": 200001,\"msg\": \"success\",\"data\": [{\"name\":\"张三\",\"age\": \"21\",\"tel\": \"1539324****\",\"balance\": \"100\"}]}";

//json字符串转jsonMap
    var jsonMap = json.decoder.convert(jsonData);
//jsonMap转model
    BaseJson<List<MyData>> baseJson = BaseJson.fromJson(jsonMap, (json) {
//判断json是不是数组类型
      if (json is List) {
//map操作符遍历生产model
        return json.map((e) => MyData.fromJson(e)).toList();
      }
//其它情况返回个空数组
      return [];
    });

//model转jsonMap
    print(baseJson.toJson((value) => value.map((value) => value.toJson()).toList()));
44.Waiting for another flutter command to release the startup lock
当执行flutter pub get时遇到 Waiting for another flutter command to release the startup lock
执行 killall -9 dart

https://stackoverflow.com/questions/51679269/waiting-for-another-flutter-command-to-release-the-startup-lock
45.flutter有用的小技巧
1.字符串可以进行加和乘,但是减和除不行 例如 String string = "a"; string*2 就是 aa
2.隐藏组件 Visibility 和 Offstage https://juejin.cn/post/6953483034305921037
3.多个请求依赖, 仅用于多个异步操作都完成后, 执行其它操作 Future.wait用法   https://www.jianshu.com/p/f1460037d008
46.Dart 格式化DateTime 为字符串
需要导入intl库
flutter pub add intl
DateFormat("yyyy-MM-dd").format(dateTime);
47.context.select 和 Selector的区别
LayoutBuild((context,_) {
 final title = context.select<VM,String>((value) => value.title);
 return Text(title);
})

Selector<VM,String>(select:(context,value) => value.title, builder:(context, value,child) {
 return Text(value);
})

context.select会在当前的作用域进行更新
48.在initState中设置状态栏颜色不生效问题
在initState中直接设置状态栏颜色不生效,可使用WidgetsBinding.instance?.addPostFrameCallback在里面设置

WidgetsBinding.instance?.addPostFrameCallback((_) {
      SystemChrome.setSystemUIOverlayStyle(
        SystemUiOverlayStyle(
          statusBarColor: AppColor.c209090,
          systemNavigationBarDividerColor: AppColor.c209090,
        ),
      );
    });
49.isolate的使用
1.https://www.jianshu.com/p/4b2fc461f3a8
2.https://www.jianshu.com/p/a524620e4bb5  (包含 exit 和 send 的区别及用法)
3.https://juejin.cn/post/7048844082348556295 
4.https://www.jianshu.com/p/e621bb554d4f
50.listview性能优化
https://juejin.cn/post/6940134891606507534
https://github.com/LianjiaTech/keframe
51.Dart Extends 无法自动引入
貌似是官方一个 Bug

可以通过一个空的类 导入所有扩展引入 其他地方通过这个引入这个空的类 调用即可

extends HelloString on String {
  String hello() => "Hello $this";
}

final name = "king";
final helloName = name.hello(); /// 这个地方报
错 不会自动引入头文件

可以通过下面方式解决
/// 第一步 打出 HelloString 自动导入 HelloString 对应的文件
/// 此时再调用 
name.hello(); /// 已经不会报错
52.监听键盘弹起和收回
https://juejin.cn/post/6960858835523207181
记得一定要销毁,不然再次进入页面会出问题

class YourTextFieldState extends State<YourTextFieldState> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    /// 初始化
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void didChangeMetrics() {
    super.didChangeMetrics();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      setState(() {
          if(MediaQuery.of(context).viewInsets.bottom==0){
            /// 键盘收回    
           }else{
            /// 键盘弹出
          } 
      });
    });
  }

  @override
  void dispose() {
    /// 销毁
    super.dispose();
    WidgetsBinding.instance.removeObserver(this);
  }
}
53.快速遍历并修改数组导致崩溃的原理

54.mounted 的使用原则

上一篇下一篇

猜你喜欢

热点阅读