Provider

状态管理

  • 状态管理就是一些变量的管理,而这些变量需要在多个路由界面中重复使用,所以就有了状态管理
  • 如果多个界面需要重复数据时,当这些界面频繁跳转时,没有全局状态管理,那就需要每次跳转 路由界面都需要传值一次达到保存数据的目的,当有了全局状态管理,每次需要读取或者改变这些数据时,则可以调用公用方法获取或修改,因此可以大大减少工作量并提升应用性能

Provider

  • 首先引入依赖并执行flutter pub get安装
1
2
dependencies:
provider: ^6.0.2
1
import 'package:provider/provider.dart';
  • lib下创建provider文件夹,里面存放状态文件,比如创建CountProvider.dart用于保存计数数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';

//继承ChangeNotifier后,可以通知所有订阅者
class CounterModel with ChangeNotifier {
int _count;//要保存的数据,我这里实现计数器,所以只有一个int变量
CounterModel(this._count);

void add() {//提供全局方法,让全局计数+1
_count++;
notifyListeners(); //当数值改变后,通知所有订阅者刷新ui
}

get count => _count; //提供全局get方法获取计数总值
}
  • 修改Flutter初始项目main.dart,实现计数功能
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import 'package:flutter/material.dart';
import 'package:flutter_demo/provider/CountProvider.dart';
import 'package:provider/provider.dart';
void main() {
runApp(
ChangeNotifierProvider(//全局状态设置
create: (context) => CounterModel(0),//创建一个countermodel全局状态类,管理count值
child: MyApp(),
),
);
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Provider'),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key ?key, required this.title}) : super(key: key);
final String title;

@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
//以下代码Provider.of<model类名>(context).属性值
//请注意,属性值在model类中必须有get方法
"${Provider.of<CounterModel>(context).count}",
style: Theme.of(context).textTheme.headline4,//字体样式
),
],
),
),
floatingActionButton: FloatingActionButton(//创建一个悬浮按钮
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}

void _incrementCounter() {//悬浮按钮点击事件
//context.read<model类名>().model中的方法;
context.read<CounterModel>().add();
}
}
  • 这时候我们发现,我们没有传值更没有返回值,就能轻松两个界面管理一个数据,是不是效率高了很多;但是我们发现,只能全局管理一个状态,那么怎么管理多个状态呢?只需要修改包裹的组件即可
1
2
3
4
5
6
7
8
9
10
11
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => CounterModel(0)),
//可以继续添加,语法如上,这样可以全局管理多个状态
],
child: MyApp(),
),
);
}

网络请求

Dio框架

  • 首先引入依赖并执行flutter pub get安装,出错可以重启项目
1
2
dependencies:
dio: ^4.0.4
  • 通过按钮触发请求,其它用法可以查询文档
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
40
41
42
43
44
45
46
47
48
49
50
51
52
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(appBar: AppBar(title: Text('接口测试')), body: DioDemo()),
);
}
}

class DioDemo extends StatefulWidget {
const DioDemo({Key? key}) : super(key: key);

@override
_DioDemoState createState() => _DioDemoState();
}

class _DioDemoState extends State<DioDemo> {
Dio _dio = new Dio();

@override
void initState() {
// TODO: implement initState
super.initState();
// 设置基地址
_dio.options.baseUrl = "https://autumnfish.cn/";
// 设置头部
_dio.options.headers = {"token":"dawadadaxca&we12"};
}

@override
Widget build(BuildContext context) {
return Column(
children: [
RaisedButton(onPressed: _get, child: Text('GET请求')),
],
);
}

void _get() async {
// 完整API: https://autumnfish.cn/search?keywords=周杰伦
var result = await _dio.get("search", queryParameters: {"keywords": "周杰伦"});
print(result);
}
}

项目打包

配置信息

  • 想配置APP的图片,你需要找到下面的目录:
1
项目根目录/android/app/src/main/res/
  • 进入之后你会看到很多mipmap-为前缀命名的文件夹,后边的是像素密度,可以看出图标的分辨率
1
2
3
4
5
mdpi (中) ~160dpi
hdpi (高) ~240dip
xhdpi (超高) ~320dip
xxhdpi (超超高) ~480dip
xxxhdpi (超超超高) ~640dip
  • 将对应像素密度的图片放入对应的文件夹中,图片记得用png格式,记得名字要统一,才能一次性进行配置
  • AndroidManifest.xml 这个文件主要用来配置APP的名称、图标和系统权限,所在的目录在
1
项目根目录/android/app/src/main/AndroidManifest.xml
1
2
android:label="flutter_app"   //配置APP的名称,支持中文
android:icon="@mipmap/ic_launcher" //APP图标的文件名称

生成Keystore

  • 首先使用命令找到keytool.exe的位置
1
flutter doctor -v

  • 这时候你直接拷贝命令并进行输入,但这里也有个坑,就是如果文件夹中间带有空空,你需要用带引号扩上;我们还要更换一个有写权限的目录。我们把命令改成了下面的形式
1
D:\'Android Studio'\jre\bin\keytool -genkey -v -keystore D:\key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key
  • 然后配置密钥库口令,回车即可生成key.jks;D盘下面就会有一个Jks的文件,记住这个文件不能共享给任何人
  • 有了这个key.jks文件后,可以到项目目录下的android文件夹下,创建一个名为key.properties的文件,并打开粘贴下面的代码
1
2
3
4
storePassword=123456    //输入上一步创建KEY时输入的 密钥库 密码
keyPassword=123456 //输入上一步创建KEY时输入的 密钥 密码
keyAlias=key
storeFile=D:/key.jks //key.jks的存放路径

配置key注册

  • key生成好后,需要在build.gradle文件中进行配置。这个过程其实很简单,就是粘贴复制一些东西,你是不需要知道这些文件的具体用处的
  • 进入项目目录的/android/app/build.gradle文件,在android{这一行前面,加入如下代码
1
2
3
def keystorePropertiesFile = rootProject.file("key.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
  • 把如下代码进行替换
1
2
3
4
5
buildTypes {
release {
signingConfig signingConfigs.debug
}
}
  • 替换成的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
  • 最后直接在终端中输入
1
flutter build apk
  • 然后安装在虚拟机上
1
flutter install