开发进度图

遇到的坑总结🔨

❌flutter打包后没有网络,即接口都无法访问,Image.network也同样失效

✅找到文件flutter项目/android/app/src/main/AndroidManifest.xml,在最底部</manifest>前加上

1
2
3
4
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 访问电话状态 -->
<uses-permission android:name="android.permission.INTERNET" /> <!-- 允许全部网络访问 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 获取网络信息状态 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 获取当前WiFi接入的状态以及WLAN热点的信息 -->

❌Widget不在屏幕可视视野内的时候会被flutter销毁Widget释放内存

✅可以使用AutomaticKeepAliveClientMixinmixin 保证当前Widget树缓存起来不被内存机制清除

1
2
// 只适合业务逻辑/交互比较简单, 单纯的组件,复杂组件参考2
class _YouWidgetState extends State<YouWidget> with AutomaticKeepAliveClientMixin

参考别人的解决方案EN

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 业务逻辑/交互比较多比较复杂的
// 把要缓存的widget里的属性转移给父组件管理来管理state,子组件只负责从父组件获取data进行widget渲染
// 这样虽然子组件移出视野被消除后,能确保数据data不被消除,也就不需要再重新请求数据的动作

class ParentWidget extends State<SomeParent> {
List<Model> models;

//.......

@override
Widget build(BuildContext context) {
return _buildListView(models, _scrollController);
}

Widget _buildListView(List<PhotoModel> models, ScrollController scrollController) {
return ChildWidget(models[0].likeBtnClickedListener,models[1].followBtnClickedListener);
}
}

class ChildWidget extends StatelessWidget{

ChildWidget(
//...,
this.likeBtnClickedListener,
this.followBtnClickedListener,
)
//...
Widget build(BuildContext context){
return Card(
//.......
IconButton(
onPressed: likeBtnClickedListener,
),
IconButton(
onPressed: followBtnClickedListener,
),
)
}
}

❌Scaffold.appBar.title的两边留空,

AppBar(titleSpacing: 0.0,)可以使appBar.title两边不留间隙,
查看官方文档不难发现Defaults to [NavigationToolbar.kMiddleSpacing]
在组件任何地方都可以调用NavigationToolbar.kMiddleSpacing来获取当前appBar的两边边距


❌Random().next()随机数无法指定min-max范围

✅ 参考随机生成100-999的函数

1
2
3
4
5
6
7
// 随机生成 $min00 - $max99 的数字
// minUnit最小个位数 maxUnit最大个位数
randomHeight({minUnit: 1, maxUnit: 9}) {
var f = Random().nextInt(9);
var m = Random().nextInt(99);
return int.parse('${f > minUnit ? f < maxUnit ? f : maxUnit : minUnit}${m < 10 ? '00' : m}');
}

❌随机生成中文Unicode并转中文

✅ 参考(这个中文生的乱七八糟😥)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 随机生成基本汉字 库量20902字
// 编码库范围 4E00-9FA5
String randomChinese({minLength: 1, maxLength: 1}) {
String str = '';
// 19968
final int _min = int.parse('0x4E00');
// 40869
final int _max = int.parse('0x9FA5');
_r(max) {
return Random().nextInt(max);
}

var ma = _r(maxLength);
// 最少要多少长度
ma = ma > minLength ? ma : minLength;
for (var i = 0; i < ma; i++) {
var r = Random().nextInt(_max);
// 如果大于第一个汉字编码
if (r > _min) {
str = str + String.fromCharCode(r);
} else {
var a = _r(4), b = _r(9), c = _r(9), d = _r(9), e = _r(9);
// 范围一定在1-4
a = a >= 1 && a <= 4 ? a : 1;
// 第一位达到最大的情况下,b不能超过0
b = a == 1 ? 9 : a >= 4 ? 0 : b;
c = a == 1 ? 9 : a >= 4 ? c <= 8 ? c : 8 : c;
d = a == 1 ? d >= 6 ? d : 6 : a >= 4 ? d <= 6 ? d : 6 : d;
e = a == 1 ? e >= 8 ? e : 8 : a >= 4 ? e <= 9 ? e : 9 : e;
str = str + String.fromCharCode(int.parse('$a$b$c$d$e'));
}
}
return str;
}

❌Scaffold.body通过bottomNavigationBar切换数组里的widget时,切换后无法缓存原来的body

body:child替换位body:IndexedStack(...)

1
2
3
4
5
6
7
8
9
10
/**
* IndexedStack继承自Stack,
* 它的作用是显示第index个child,
* 其它child在页面上是不可见的,但所有child的状态都被保持,所以这个Widget可以实现我们的需求,
* 我们只需要将现在的body用IndexedStack包裹一层即可
*/
body: IndexedStack(
index: this._current,
children: this._widgets,
),