Flutter
Flutter
第二版序 | 《Flutter实战·第二版》 (flutterchina.club)
万字长文,为你送上全网最全Flutter学习资料! - 掘金 (juejin.cn)
官网:Flutter 开发文档 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter
http://www.laomengit.com/guide/introduction/mobile_system.html
flutter属于声名式UI
视频学习:发送网络请求_哔哩哔哩_bilibili
周糖果 的个人主页 - 搜索 - 掘金 (juejin.cn)
包下载网站
导入包:
1 flutter pub add 包名
示例: flutter pub add http
2 pubspec.yaml 在 dependencies 下加入 依赖包名和版本, 运行 flutter pub get
示例:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
http: ^1.1.2
3 导包
import 'package:http/http.dart' as http;
关于import
1、as关键字- 给库起别名!目的:防止类名方法名冲突!
2、导入库,默认是整个文件中的都会导入
*shoW:执行需要导入的内容
*hide:需要隐藏的内容。
pubspec.yaml 配置文件
【Flutter 实战】pubspec.yaml 配置文件详解 - 掘金 (juejin.cn)
flutter安装
下载:Flutter SDK archive | Flutter
双击flutter安装目录下的flutter_console.bat执行安装, 完成后就能运行flutter命令了
设置环境变量: Path 增加安装路径,目录到安装包bin的路径
android studio
https://blog.csdn.net/qq_43231248/article/details/132591061
下载flutter插件
配置sdk , 命令行 flutter doctor -v 第一行显示 sdk 路径
Android实战——正确配置 XXX_HOME 环境变量_androidhome配置-CSDN博客
vscode
vscode 启动与调试 flutter 项目 - 掘金 (juejin.cn)
flutter+vscode+独立运行AVD 环境搭建(新手入坑记录)-CSDN博客
初识flutter,遇到Android sdkmanager tool not found-CSDN博客
https://blog.csdn.net/weixin_43840202/article/details/110353440
解决 找到文件 C:\Users\�ŷ�.gradle\wrapper\dists\gradle-7.5-all\6qsw290k5lz422uaf8jf6m7co\gradle-7.5-all.zip
所有gradle版本下载地址
https://services.gradle.org/distributions/ 镜像网站: https://mirrors.cloud.tencent.com/gradle/
一定要下载对应的版本,替换掉文件
gradle-wrapper.ropeties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
最终android目录下 build.gradle如下所示
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
// google()
mavenCentral()
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/jcenter' }
maven { url 'https://maven.aliyun.com/nexus/content/groups/public' }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
vscode热更新
flutter run 运行程序
在终端中,按下快捷键 R 或输入 r
来触发 Flutter 的热重载功能。你也可以使用 VSCode 的内置菜单栏中的“调试”选项来执行热重载。
dart
渲染
Flutter渲染原理解析:https://zhuanlan.zhihu.com/p/135969091
在Flutter中,Widget是Flutter用户界面的基本构成单元,可以说一切皆Widget。
widget
两种 widget 类型 : 有状态 stateful 无状态的 stateless
页面基础
import 'package:flutter/material.dart';
// 一定要导入dart包,同时通过 void main()执行runApp才能进行下一步的操作。
void main() {
runApp(const App());
}
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
// build方法返回小部件
Widget build(BuildContext context) {
return MaterialApp(
// 是否显示 debug 标识
debugShowCheckedModeBanner: false,
home: Scaffold(
// appBar在app头部显示
appBar: AppBar(
centerTitle: true, // 标题居中
title: const Text('hello flutter'), // 设置标题
backgroundColor: const Color(0xFF73D2FF)), //标题背景色
// bottomNavigationBar 底部导航
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
// 选中的导航的颜色
fixedColor: Colors.green,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: '首页',
),
BottomNavigationBarItem(
icon: Icon(Icons.favorite),
label: '收藏',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: '设置',
),
],
currentIndex: 0,
),
// body里面放入自定义的widget,主体内容
body: const MyWidget(),
));
}
}
// 自定义的 MyWidget 部件 (此处是无状态的)
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
// build方法返回小部件
Widget build(BuildContext context) {
return const Center(
child: Text(
'hello world1234567890',
// 文字方向
textDirection: TextDirection.ltr,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.blue,
backgroundColor: Colors.red),
));
}
}
AppBar的一些参数设置
Flutter 控件 AppBar的一些参数设置_flutter appbar 参数-CSDN博客
创建car类(列表排列【竖着】Demo)
car.dart
class Car {
const Car({this.name, this.imageUrl});
final String? name;
final String? imageUrl;
}
main.dart 渲染出一个竖着排列的列表
import 'package:flur/model/Car.dart';
class Home extends StatelessWidget {
const Home({super.key});
// 加了_是指内部访问(指的是文件的内部)
Widget itemForRow(BuildContext context, int index) {
return Container(
color: Colors.white,
margin: const EdgeInsets.all(10),
child: Column(
children: [
Image.network(datas[index].imageUrl!),
Container(
height: 10,
),
Text('$index ${datas[index].name}')
],
));
}
Widget build(BuildContext context) {
return Center(
child: ListView.builder(
itemCount: datas.length,
itemBuilder: itemForRow,
),
);
}
}
final List<Car> datas = [
const Car(
name: '车子1',
imageUrl:
'https://img.zcool.cn/community/01903158b7a4e7a801219c773f0437.jpg@2o.jpg'),
const Car(
name: '车子2',
imageUrl:
'https://img.zcool.cn/community/01903158b7a4e7a801219c773f0437.jpg@2o.jpg'),
const Car(
name: '车子3',
imageUrl:
'https://img.zcool.cn/community/01903158b7a4e7a801219c773f0437.jpg@2o.jpg'),
const Car(
name: '车子4',
imageUrl:
'https://img.zcool.cn/community/01903158b7a4e7a801219c773f0437.jpg@2o.jpg'),
const Car(
name: '车子4',
imageUrl:
'https://img.zcool.cn/community/01903158b7a4e7a801219c773f0437.jpg@2o.jpg'),
];
一个带有底部导航的页面切换Demo(仿vx)
main.dart提供了底部导航,通过修改 _currentIndex的值实现不同的底部导航按钮的高亮显示,body的内容通过 _pages 的列表根据不同的 _currentIndex 的值实现 widget 的切换
find.dart里面的 _DiscoverCell 类里面有 widget属性 可以获取到 DiscoverCell 类里面的属性
main.dart
import 'package:flur/tabBar/connect.dart';
import 'package:flur/tabBar/find.dart';
import 'package:flur/tabBar/mine.dart';
import 'package:flur/tabBar/rootWedget.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
Widget build(BuildContext context) {
return MaterialApp(
title: '我的一个flutter',
// 是否显示 debug 标识
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
// 修改点击底部导航的点击效果
highlightColor: const Color.fromRGBO(1, 0, 0, 0.0),
splashColor: const Color.fromRGBO(1, 0, 0, 0.0)),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
// 选中的底部导航
int _currentIndex = 0;
// 选中的底部导航切换的widget
final List<Widget> _pages = [
const rootWidget(),
const connectWidget(),
const findWidget(),
const mineWidget()
];
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// backgroundColor: const Color(0xFF73D2FF),
// title: const Text('hello flutter'),
// centerTitle: true, // 标题居中
// ),
// bottomNavigationBar 底部导航
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed,
fixedColor: Colors.green,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: '首页',
),
BottomNavigationBarItem(
icon: Icon(Icons.favorite),
label: '通讯录',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: '发现',
),
// 使用自定义的底部导航栏图片
BottomNavigationBarItem(
// 默认的 icon 样式
icon: Image(
image: AssetImage('images/my.png'),
width: 20,
height: 20,
),
// 高亮时的 icon 样式
activeIcon: Image(
image: AssetImage('images/mineHeader.png'),
width: 20,
height: 20,
),
label: '我的',
),
],
currentIndex: _currentIndex,
// 点击切换选中的导航栏
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
),
body: _pages[_currentIndex]);
}
}
rootWidget.dart
import 'package:flutter/material.dart';
class rootWidget extends StatefulWidget {
const rootWidget({super.key});
State<rootWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<rootWidget> {
Widget build(BuildContext context) {
// ignore: dead_code
return Scaffold(
appBar: AppBar(
backgroundColor: const Color(0xFF73D2FF),
title: const Text('首页'),
centerTitle: true, // 标题居中
),
body: const Center(
child: Text('首页'),
),
);
}
}
connectWidget.dart
import 'package:flutter/material.dart';
class connectWidget extends StatefulWidget {
const connectWidget({super.key});
State<connectWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<connectWidget> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: const Color(0xFF73D2FF),
title: const Text('通讯录'),
centerTitle: true, // 标题居中
),
body: const Center(
child: Text('通讯录'),
),
);
}
}
find.dart (发现页面)
// ignore_for_file: unnecessary_null_comparison
import 'package:flur/tabBar/findChild.dart';
import 'package:flutter/material.dart';
class findWidget extends StatefulWidget {
const findWidget({super.key});
State<findWidget> createState() => _findWidgetState();
}
class _findWidgetState extends State<findWidget> {
final Color _themeColor = const Color.fromRGBO(220, 220, 220, 1.0);
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: _themeColor,
title: const Text('发现'),
centerTitle: true, // 标题居中
),
body: Container(
height: 800,
color: _themeColor,
child: ListView(
children: [
const DiscoverCell(
title: '欢迎1111',
subTitle: '副标题',
imageName: 'images/mineHeader.png',
subImageName: 'images/mineHeader.png',
),
const SizedBox(height: 10),
const DiscoverCell(
title: '欢迎22',
subTitle: '副标题',
imageName: 'images/mineHeader.png',
subImageName: 'images/mineHeader.png',
),
Container(
color: const Color.fromARGB(255, 223, 253, 224),
height: 0.5,
),
const DiscoverCell(
title: '欢迎33',
subTitle: '副标题',
imageName: 'images/mineHeader.png',
subImageName: 'images/mineHeader.png',
),
const SizedBox(height: 10),
const DiscoverCell(
title: '欢迎4',
subTitle: '副标题',
imageName: 'images/mineHeader.png',
subImageName: 'images/mineHeader.png',
),
],
)));
}
}
// 有状态的widget
class DiscoverCell extends StatefulWidget {
final String title;
final String subTitle;
final String imageName;
final String subImageName;
const DiscoverCell(
{super.key,
required this.title,
this.subTitle = '',
required this.imageName,
this.subImageName = ''})
// assert提醒,可以没有
: assert(title != null, 'title不能为空'),
assert(imageName != null, 'imageName不能为空');
State<DiscoverCell> createState() => _DiscoverCell();
}
class _DiscoverCell extends State<DiscoverCell> {
Color _currentColor = Colors.white; //控制手势操作时的颜色
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
// 跳转到findChildWidget,新页面,并且携带参数 title
Navigator.of(context)
.push(MaterialPageRoute(builder: (BuildContext context) {
print(widget.title);
return findChildWidget(title: widget.title);
}));
setState(() {
setState(() {
_currentColor = Colors.white;
});
});
},
onTapDown: (TapDownDetails details) {
print("点下了");
setState(() {
_currentColor = const Color.fromARGB(255, 245, 242, 242);
});
},
onTapCancel: () {
print("点下离开");
setState(() {
_currentColor = Colors.white;
});
},
child: Container(
color: _currentColor,
child: Row(
// 主轴方向
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// left
Container(
height: 40,
padding: const EdgeInsets.all(10),
child: Row(
children: [
const Image(
image: AssetImage('images/mineHeader.png'),
width: 30,
height: 30,
),
// 间隙
const SizedBox(
width: 15,
),
Text(widget.title)
],
),
),
//right
Container(
height: 40,
padding: const EdgeInsets.all(10),
child: Row(
children: <Widget>[
if (widget.subTitle != null)
Text(widget.subTitle)
else
const Text(''),
// 间隙
const SizedBox(
width: 15,
),
widget.subImageName != null && widget.subImageName != ''
? Image.asset(widget.subImageName)
: Container(),
// Image(image: image)
const Icon(
Icons.arrow_forward_ios,
size: 14,
)
],
),
)
],
),
));
}
}
mineWidget.dart
import 'package:flutter/material.dart';
class mineWidget extends StatefulWidget {
const mineWidget({super.key});
State<mineWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<mineWidget> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: const Color(0xFF73D2FF),
title: const Text('我的'),
centerTitle: true, // 标题居中
),
body: const Center(
child: Text('我的'),
),
);
}
}
flutter获取屏幕宽高
flutter 获取屏幕宽高 - 简书 (jianshu.com)
1.系统提供的方法
这个方法可以得到当前控件的宽,高
final size =MediaQuery.of(context).size;
final width =size.width;
final height =size.height;
2.使用起来最简单的方法
使用这个方法,能够快速的得到屏幕的宽高
import 'dart:ui';
final width = window.physicalSize.width;
final height = window.physicalSize.height;
基本widget,基本使用设置
Container
使用示例:
Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.only(left: 10),
color: wechatThemeColor,
height: 20 ,
child: Text( "微信号:1234567", style: TextStyle(color: Colors.grey))
),
container里面文字居左
alignment: Alignment.centerLeft,
Text(Flutter文本显示和样式)
Flutter Text(Flutter文本显示和样式)_flutter text strutstyle-CSDN博客
示例:
Text( "微信号:1234567", style: TextStyle(color: Colors.grey))
文字超出显示省略号
Text('12345',
maxLines: 1, //最多显示几行
overflow: TextOverflow.ellipsis, // 超出显示省略号
)
图片的使用
Flutter 中 Image 的 5 种加载方式详解 | Flutter Widgets - 掘金 (juejin.cn)
Flutter入门(9):Flutter 组件之 Image、AssetImage 详解 - 简书 (jianshu.com)
项目根目录下创建 images 文件夹用于存放图片文件
Image(
image: AssetImage('images/mineHeader.png'),
width: 20,
height: 20,
),
Image(
image: NetworkImage(imageUrl),
width: 20,
height: 20,
),
使用到的图片文件路径需要在 pubspec.yaml 中进行配置
flutter:
assets:
- images/
flutter颜色设置
常规用法 Flutter颜色(Color)使用和十六进制颜色转换 - 简书 (jianshu.com)
1、Color c1 = Color(0xFF3CAAFA);
2、Color c2 = Color.fromRGBO(60, 170, 250, 1);
3、Color c3 = Color.fromARGB(255, 60, 170, 250);
4、Color c5 = Colors.blue;
- 1、
Color(int value)
Color(0xFF3CAAFA),value接收的是一个十六进制(0x开头),FF表示的是十六进制透明度(00-FF),3CAAFA是十六进制色值。 - 2、
Color.fromRGBO(int r, int g, int b, double opacity)
*Color.fromRGBO(60, 170, 250, 1)*,r、g、b分别表示red、green、blue,常规的红绿蓝三色,取值范围为0-255,opacity表示透明度,取值0.0-1.0。 - 3、
Color.fromARGB(int a, int r, int g, int b)
*Color.fromARGB(255, 60, 170, 250)*,a表示透明度,取值0-255,rgb同上一样。 - 4、
Colors._()
Colors类定义了很多颜色,可以直接使用,例如 Colors.blue,其实就是第一种***Color(int value)***的封装。
Icon图标
示例:
Icon(
Icons.audiotrack,
color: Colors.green,
size: 30.0,
),
GestureDetector处理用户手势操作
Flutter 手势系列教程---GestureDetector - 掘金 (juejin.cn)
示例:
GestureDetector(
// 点击触发
onTap: () {
print("添加联系人");
// 页面跳转
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
const findChildWidget(title: "添加联系人")));
},
child: Container(
margin: const EdgeInsets.only(right: 10),
child: const Icon(
Icons.person_add_alt_1_rounded,
size: 30.0,
),
))
Navigator 导航 页面跳转
Flutter导航Navigator使用详解 - 掘金 (juejin.cn)
示例:
// 页面跳转 跳转到 findChildWidget 携带的参数为 title
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
const findChildWidget(title: "添加联系人")));
ListView 列表组件之一
Flutter 中 ListView 的 4 种构建方式详解 | Flutter Widgets - 掘金 (juejin.cn)
基础列表 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter
示例:
ListView(
children: const <Widget>[
ListTile(
leading: Icon(Icons.map),
title: Text('Map'),
),
ListTile(
leading: Icon(Icons.photo_album),
title: Text('Album'),
),
ListTile(
leading: Icon(Icons.phone),
title: Text('Phone'),
),
],
),
PopupMenuButton
appBar 右上角的按钮
Flutter | 超实用简单菜单弹出框 PopupMenuButton - 掘金 (juejin.cn)
示例:
appBar: AppBar(
backgroundColor: const Color(0xFF73D2FF),
title: const Text('首页'),
centerTitle: true, // 标题居中
actions: [
Container(
margin: const EdgeInsets.only(right: 10),
child: PopupMenuButton(
color: Colors.white,
offset: const Offset(0, 40),
itemBuilder: (BuildContext context) {
return <PopupMenuItem<String>>[
const PopupMenuItem(child: Text('发起群聊1')),
const PopupMenuItem(child: Text('发起群聊2')),
const PopupMenuItem(child: Text('发起群聊3'))
];
},
child: const Icon(
Icons.add_circle_outline,
size: 30.0,
),
),
)
],
),
生命周期
Flutter 的生命周期 - 掘金 (juejin.cn)
看到的开源项目
使用 Flutter 实现音视频剪辑项目 App (qq.com)
网络请求
制造接口数据
Mock.js (mockjs.com) 数据生成匹配规则在这里查看
虚拟数据获取,如虚拟头像,匹配规则按照路径修改 Random User Generator | Documentation
http
获取网络数据 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter
安装包: flutter pub add http
我使用的dart页面示例:
接口返回虚拟数据:
{
"chatList": [
{
"imageUrl": "https://randomuser.me/api/portraits/men/49.jpg",
"name": "曹明",
"message": "步安保集西关别八解那看局总须由心基。离接思受被体易部之内做保器区广具。并切名便目子信才观处后带建证。称例儿带些难不极下我斯己求然一难半。"
},
{
"imageUrl": "https://randomuser.me/api/portraits/men/38.jpg",
"name": "田平",
"message": "往参给参领相须越面调支关主后比。关命产议断华力历现价导员维小太参关有。国导会名世消六身划工通外油题最他第。"
}
]
}
获取数据
import 'package:flutter/material.dart';
// 导包
import 'package:http/http.dart' as http;
// json转换
import 'dart:convert';
// Map 转 json json.encode( Map );
// json 转 Map json.decode( Map );
class Chat {
final String? name;
final String? message;
final String? imageUrl;
Chat({this.name, this.message, this.imageUrl});
// 工厂构造
factory Chat.fromMap(Map map) {
return Chat(
name: map['name'],
message: map['message'],
imageUrl: map['imageUrl'],
);
}
static formMap(String body) {}
}
// 网络请求是异步请求,注意 async await 的使用
getDatas() async {
final url =
Uri.parse("http://rap2api.taobao.org/app/mock/315841/getMessageList");
final response = await http.get(url);
// print('Response status: ${response.statusCode}');
// print('Response body: ${response.body}');
// json 转 Map 再转模型数据
final chatModle = Chat.fromMap(json.decode(response.body));
}
由于获取数据是异步的,实现异步渲染,getDatas 还要进一步修改
Future
是 Dart 用来处理异步操作的一个核心类,它通常代表一个可能的值或者将来或许会用到的错误。
Future<List<Chat>> getDatas() async {
final response = await http.get(
Uri.parse('http://rap2api.taobao.org/app/mock/315841/getMessageList'));
if (response.statusCode == 200) {
// 获取响应数据,转成map类型
final responseBody = json.decode(response.body);
// map作为 list的遍历方法
List<Chat> chatList = responseBody['chatList']
.map<Chat>((item) => Chat.fromMap(item))
.toList();
return chatList;
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception(
'statusCode: ${response.statusCode}' 'Failed to load chat');
}
}
数据渲染(FutureBuilder 渲染)
Container(
child: FutureBuilder(
future: getDatas(), //这里发起数据请求
builder: (BuildContext context, AsyncSnapshot snapshot) {
print(snapshot.data); // 获取到的数据
print(snapshot.connectionState); //获取数据状态
// 正在加载
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: Text("加载中"),
);
}
// 加载完毕
return ListView(
children: snapshot.data.map<Widget>((Chat item) {
return return ListTile(
title: Text(
item.name,
),
subtitle: Text(item.message,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 10, color: Colors.blueGrey)),
leading: CircleAvatar(
backgroundImage: NetworkImage(item.imageUrl),
),
);
}).toList(),
);
}),
)
数据获取后渲染(ListView渲染)
List<Chat> _datas = [];
void initState() {
// TODO: implement initState
super.initState();
// 获取网络数据
getDatas()
.then((List<Chat> datas) => {
setState(() {
_datas = datas;
})
})
.catchError((e) => {print(e)})
.whenComplete(() => {print('获取数据完毕')});
}
// 渲染数据
Container(
child: Container(
child: _datas.isEmpty
? const Center(child: Text("loading..."))
: ListView.builder(
itemCount: _datas.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(
_datas[index].name,
),
subtitle: Text(_datas[index].message,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 10, color: Colors.blueGrey)),
leading: CircleAvatar(
backgroundImage:
NetworkImage(_datas[index].imageUrl),
),
);
}))));
dio
dio/dio/README-ZH.md at main · cfug/dio · GitHub
11.3 Http请求库-dio | 《Flutter实战·第二版》 (flutterchina.club)
Mixins 混入
目的:给一个类增加功能。是多继承。(我还不太清楚)
Flutter 语法进阶 | 深入理解混入类 mixin - 掘金 (juejin.cn)
异常处理
异步处理 多线程
Flutter-Dart中的异步和多线程讲解 - 掘金 (juejin.cn)
flutter 生命周期
Flutter 的生命周期 - 掘金 (juejin.cn)