- 完整的Demo流程 ✅
- 常用的组件有哪些 ✅
- 页面导航 ✅
- 包依赖管理 ✅
- 资源引用 ✅
- 数据持久化 ✅
- 网络请求 ✅
- json解析 ✅
- 原生调用的Flutter模块 ✅
- 开发Flutter包和插件 ✅
完整的Demo流程
- ChangeNotifier 设置应用状态。
- ChangeNotifierProvider 应用状态提供给整个应用,任何组件都可以获取状态。
- SafeArea确保显示不会被凹槽的状态栏挡住。
[WordPair? pair]
加[]表示是可选的参数。
常用组件
MaterialApp 主题
Scaffold 脚手架
NavigationRail 导航组件
LayoutBuilder 布局伸缩通知变化
SizedBox、Spacer空白占用
Padding 间距
Text文本
Icon图标
TextButton.icon 文本+图标 按钮
Row和Column 行列列表
GridView 网格,ListTile网格项
页面导航
1 | //0.安装&依赖 |
资源引用
1 | //pubspec.yaml 配置文件 |
- 分辨率 1.0x、2.0x这种方式引用,v3.24.3会提示找不到异常,先忽略。
- svg加载
1
2
3
4flutter pub add flutter_svg
//加载
import 'package:flutter_svg/flutter_svg.dart';
SvgPicture.asset('assets/images/a4.svg',semanticsLabel: "icon",) - 字符串国际化
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//1.搜索插件 Flutter Intl
//2.添加依赖
dependencies:
flutter_localizations:
sdk: flutter
intl: any
flutter:
generate: true
//3.根目录新建文件l10n.yaml,arb就是json文件,添加以下内容:
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
//4.运行 flutter run 命令,你将在 ${FLUTTER_PROJECT}/.dart_tool/flutter_gen/gen_l10n 中看到生成的文件。
//5.入口添加
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
MaterialApp(
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
const Locale('en', 'US'), // 美国英语
const Locale('zh', 'CN'), // 中文简体
//其他Locales
],
locale: const Locale('en', 'US'), //手动指定locale
//6.获取当前语言
Locale myLocale = Localizations.localeOf(context);
//7.引用字符串
Text(AppLocalizations.of(context)!.welcome)
数据持久化
- key-values
1
2
3
4
5
6
7
8
9
10
11
12
13//执行
flutter pub add shared_preferences
//自动添加依赖
shared_preferences: ^2.3.2
//获取String
final prefs = await SharedPreferences.getInstance();
spDataText = prefs.getString(Constants.SP_NAME) ?? '';
//设置String
final prefs = await SharedPreferences.getInstance();
prefs.setString(Constants.SP_NAME, data);
//移除String
final prefs = await SharedPreferences.getInstance();
await prefs.remove(Constants.SP_NAME); - sqlite
1
2
3
4
5//执行
flutter pub add sqflite path
//自动添加依赖
sqflite: ^2.4.0
path: ^1.9.0
网络请求
1 | flutter pub add http |
- 添加权限
1
2
3
4//android
<uses-permission android:name="android.permission.INTERNET" />
//macos/Runner/DebugProfile.entitlements 和 macos/Runner/Release.entitlements 文件添加网络权限
<!-- Required to fetch data from the internet. --> <key>com.apple.security.network.client</key> <true/> - FutureBuilder 异步组件显示网络数据
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//Album数据类
class Album {
final int userId;
final int id;
final String title;
Album({required this.userId, required this.id, required this.title});
factory Album.fromJson(Map<String, dynamic> json) {
if (json.containsKey('userId') &&
json.containsKey('id') &&
json.containsKey('title')) {
return Album(
userId: json['userId'] as int,
id: json['id'] as int,
title: json['title'] as String,
);
} else {
throw const FormatException('Failed to load album.');
}
}
}
//http请求
Future<Album> fetchAlbum() async{
final response = await http
.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
//显示组件
late Future<Album> futureAlbum = fetchAlbum();
FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text("title=${snapshot.data!.title},userId=${snapshot.data!.userId}");
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
)
json解析
dart:convert
:内置的库,手动序列化/反序列化1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23class User{
String name = "";
int age = 0;
User();
User.fromJson(Map<String, dynamic> json)
: name = json['name'] as String,
age = json['age'] as int;
Map<String, dynamic> toJson() => {
'name': name,
'age': age,
};
}
//反序列化
var user = jsonDecode(json) as Map<String, dynamic>;
print("test 名字:${user['name']},年龄:${user['age']}");
//序列化
var userObj = User();
userObj.name = user['name'];
userObj.age = user['age'];
String json = jsonEncode(userObj);//是调用toJson方法。dart:json_serializable
:自动序列化/反序列化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//依赖
flutter pub add json_annotation dev:build_runner dev:json_serializable
//使用注解
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';
()
class User{
'name') //别名 (name:
String name = "";
int age = 0;
User();
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
//如果有dart安装不是flutter自带,使用brew删除
brew uninstall dart
//生成代码User.g.dart
flutter pub run build_runner build --delete-conflicting-outputs
//User类修改
import 'package:json_annotation/json_annotation.dart';
part 'User.g.dart'; //还是报错,但是可以运行起来✅
false) (nullable:
class User{
'name') //别名 (name:
String name = "";
int age = 0;
User();
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
原生调用的Flutter模块
Flutter模块
导出aar包:
flutter build aar
- 如果直接导包,需要查看输出的文件夹下:pom文件类似这些依赖也一并导入
1
2
3
4<groupId>io.flutter</groupId>
<artifactId>flutter_embedding_debug</artifactId>
<version>1.0.0-36335019a8eab588c3c2ea783c618d90505be233</version>
<scope>compile</scope> - 使用本地仓库依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17allprojects {
String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?:
"https://storage.googleapis.com"
repositories {
maven {
url'../../flutter_module/build/host/outputs/repo'
}
maven {
url "$storageUrl/download.flutter.io"
}
google()
mavenCentral()
}
}
//添加依赖项
debugImplementation 'dev.flutter.example.flutter_module:flutter_debug:1.0'
releaseImplementation 'dev.flutter.example.flutter_module:flutter_release:1.0'
- 如果直接导包,需要查看输出的文件夹下:pom文件类似这些依赖也一并导入
配置Flutter模块
flutter create -t module flutter_module
创建Flutter模块1
2
3
4
5
6//在pubspec.yaml下配置为Flutter模块
flutter:
module:
androidX: true
androidPackage: dev.flutter.example.flutter_module //定义android模块下唯一标识
iosBundleIdentifier: dev.flutter.example.flutterModule //定义ios模块下唯一标识集成到android或ios
1
2
3
4
5
6
7
8cd flutter_module/
flutter pub get
android打开项目即可
open -a "Android Studio" ../android_fullscreen
ios需要pod安装下再打开
cd ../ios_fullscreen
pod install
open IOSFullScreen.xcworkspace
Android
FlutterEngine
预初始化MethodChannel
methodChannel#setMethodCallHandler(..)
处理flutter回调原生methodChannel#invokeMethod(..)
处理原生调用flutterFlutterActivity
flutter页面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
43const val ENGINE_ID = "1"
private lateinit var channel: MethodChannel
var count = 0
/**Application#onCreate()下调用**/
val flutterEngine = FlutterEngine(this)
flutterEngine
.dartExecutor
.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache.getInstance().put(ENGINE_ID, flutterEngine)
channel = MethodChannel(flutterEngine.dartExecutor, "dev.flutter.example/counter")
channel.setMethodCallHandler { call, _ ->
//flutter调用原生时,接收
when (call.method) {
"incrementCounter" -> {
count++
reportCounter()
}
"requestCounter" -> {
reportCounter()
}
}
}
//...
private fun reportCounter() {
//原生调用flutter
channel.invokeMethod("reportCounter", count)
}
//跳转到Flutter 页面
val intent = FlutterActivity
.withCachedEngine(ENGINE_ID)
.build(this)
startActivity(intent)
//添加AndroidMainfest.xml声明
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
android:exported="true"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize" />android源码方式引入
1
2
3
4
5
6
7
8
9
10
11
12//build.gradle文件
dependencies {
implementation project(':flutter')
}
//settings.gradle文件,其中evaluate执行指定文件的代码
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'flutter_module/.android/include_flutter.groovy'
))
include ':flutter_module'
project(':flutter_module').projectDir = new File('../flutter_module')android aar包方式引入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17//执行
flutter build aar
//添加gradle本地依赖库地址
String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?:
"https://storage.googleapis.com"
repositories {
maven {
url
'/Users/zhangxuyang/Desktop/Demo/add-to-app/flutter_module/build/hos
t/outputs/repo'
}
maven {
url "$storageUrl/download.flutter.io"
}
}
//添加依赖库
debugImplementation("dev.flutter.example.flutter_module:flutter_debug:1.0") releaseImplementation("dev.flutter.example.flutter_module:flutter_release:1.0")//- ⚠️使用kts时,新建
flutter_settings.gradle
,再引入1
2
3
4
5
6apply { from("flutter_settings.gradle") }
/**遇到这个问题
Caused by: org.gradle.api.InvalidUserCodeException: Build was configured to prefer settings repositories over project repositories but repository ‘maven’ was added by plugin class ‘FlutterPlugin’ Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Failed to apply plugin class ‘FlutterPlugin’.
**/
//修改repositoriesMode
repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)1
2
3
4
5//异常
Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Failed to apply plugin class 'FlutterPlugin'.
//修改repositoriesMode
repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
Android-Error
使用Compose新建项目
导入flutter模块提示错误1
2
3
4
5
6
7
8
9Execution failed for task ':app:checkDebugAarMetadata'.
> Could not resolve all files for configuration ':app:debugRuntimeClasspath'.
> Could not find org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0.
Searched in the following locations:
- https://storage.googleapis.com/download.flutter.io/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.0/kotlin-stdlib-jdk8-1.9.0.pom
Required by:
project :app
> Could not find androidx.compose.ui:ui-tooling:.
Required by:
Flutter
1 | - `MethodChannel` |
iOS
FlutterEngine
flutterEngine?.run(withEntrypoint: nil)
初始化启动Flutter引擎FlutterMethodChannel
flutterMethodChannel#setMethodCallHandler(..)
设置flutter回调原生iosflutterMethodChannel?.invokeMethod(..)
设置ios调用flutterFlutterViewController
flutter页面- iOS新建项目出现上下区域黑色,改配置里面Launch Screen File 添加
LaunchScreen
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
69
70
71
72
73
74
75
76//应用入口初始化
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var flutterEngine : FlutterEngine?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Instantiate Flutter engine
self.flutterEngine = FlutterEngine(name: "io.flutter", project: nil)
self.flutterEngine?.run(withEntrypoint: nil)
return true
}
}
//UIViewController下
var methodChannel : FlutterMethodChannel?
var count = 0
if let flutterEngine = (UIApplication.shared.delegate as? AppDelegate)?.flutterEngine {
methodChannel = FlutterMethodChannel(name: "dev.flutter.example/counter",binaryMessenger: flutterEngine.binaryMessenger)
methodChannel?.setMethodCallHandler({ [weak self](call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if let strongSelf = self {
switch(call.method) {
case "incrementCounter":
strongSelf.count += 1
strongSelf.counterLabel.text = "Current counter: \(strongSelf.count)"
strongSelf.reportCounter()
case "requestCounter":
strongSelf.reportCounter()
default:
// Unrecognized method name
print("Unrecognized method name: \(call.method)")
}
}
})
}
func reportCounter() {
methodChannel?.invokeMethod("reportCounter", arguments: count)
}
@IBAction func buttonWasTapped(_ sender: Any) {
if let flutterEngine = (UIApplication.shared.delegate as? AppDelegate)?.flutterEngine {
let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
self.present(flutterViewController, animated: true, completion: nil)
}
}
//pod文件
flutter_application_path = '../flutter_module'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'IOSFullScreen' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for IOSFullScreen
install_all_flutter_pods(flutter_application_path)
target 'IOSFullScreenTests' do
inherit! :search_paths
# Pods for testing
end
target 'IOSFullScreenUITests' do
inherit! :search_paths
# Pods for testing
end
end
post_install do |installer|
flutter_post_install(installer) if defined?(flutter_post_install)
end
iOS-Error
使用SwiftUI 新建项目
导入flutter模块pod install
提示错误[!] The platform of the target
iOSAddFlutter(macOS 14.0) is not compatible with
FlutterPluginRegistrant (0.0.1), which does not support
macOS.
开发Flutter包和插件
纯Flutter的自定义包
- 创建自定义包
flutter create --template=package hello
- 通过path路径形式加载包
1
2
3dependencies:
hello:
path: ../../packgage-plugin/hello/ - 通过git形式加载包,(可通过path设置路径,默认是根目录下)
1
2
3
4
5dependencies:
packageA:
git:
url: git@github.com:flutter/packageA.git
path: packages/packageA
调用特定平台API的原生插件包
flutter create --template=plugin --platforms=android,ios -i objc hello
,其中-i
指定ios语言,-a
指定android语言 ,平台有android,ios,linux,macos,windows
- ErrorTips:新增–platforms的macos时,运行macos应用提示
error: no such module 'native_hello' import native_hello
。需要在macos重新运行pod install
重新刷新flutter插件。 Futurn.then()
,调用异步代码