2 . 数据持久化
- 由于 Flutter 仅接管了渲染层,真正涉及到存储等操作系统底层行为时,还需要依托于原生 Android、iOS.
- 三种数据持久化方法,即文件、SharedPreferences 与数据库
- Flutter 提供了两种文件存储的目录,即临时(Temporary)目录与文档(Documents)目录:
2.1 文件
需要引入: path_provider: ^1.6.4
//创建文件目录
Future<File> get _localFile async {
final directory = await getApplicationDocumentsDirectory();
final path = directory.path;
return File('$path/content.txt');
}
//将字符串写入文件
Future<File> writeContent(String content) async {
final file = await _localFile;
return file.writeAsString(content);
}
//从文件读出字符串
Future<String> readContent() async {
try {
final file = await _localFile;
String contents = await file.readAsString();
return contents;
} catch (e) {
return "";
}
}
2.2 SharedPreferences
需要引入: shared_preferences: ^0.5.6+2
//读取SharedPreferences中key为counter的值
Future<int>_loadCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int counter = (prefs.getInt('counter') ?? 0);
return counter;
}
//递增写入SharedPreferences中key为counter的值
Future<void>_incrementCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int counter = (prefs.getInt('counter') ?? 0) + 1;
prefs.setInt('counter', counter);
}
2.3 数据库
需要引入: sqflite: ^1.2.1
dbDemo() async {
final Future<Database> database = openDatabase(
//join是拼接路径分隔符
join(await getDatabasesPath(), 'student_database.db'),
onCreate: (db, version) => db.execute(
"CREATE TABLE students(id TEXT PRIMARY KEY,name TEXT,score INTEGER)"),
onUpgrade: (db, oldVersion, newVersion) {
//dosth for 升级
},
version: 1,
);
Future<void> insertStudent(Student std) async {
final Database db = await database;
await db.insert(
'students',
std.toJson(),
//插入冲突策略,新的替换旧的
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
//插入3个
await insertStudent(student1);
await insertStudent(student2);
await insertStudent(student3);
Future<List<Student>> students() async {
final Database db = await database;
final List<Map<String, dynamic>> maps = await db.query('students');
return List.generate(maps.length, (i) => Student.fromJson(maps[i]));
}
////读取出数据库中插入的Student对象集合
students().then((list) => list.forEach((s) => print(s.name)));
//释放数据库资源
final Database db = await database;
db.close();
}
3 . Flutter调原生
- 用AS单独打开Flutter项目中的Android工程,写代码,每次写完代码rebuild一下.然后想让Flutter代码能调到Android这边的代码,得重新运行.
- 如果AS run窗口不展示任何消息,可以使用 命令
flutter run lib/native/invoke_method.dart
执行dart,然后看错误消息. - Flutter发起方法调用请求开始,请求经由唯一标识符指定的方法通道到达原生代码宿主,而原生代码宿主则通过注册对应方法实现,响应并处理调用请求.最后将执行结果通过消息通道,回传至Flutter.
- 方法通道是非线程安全的,需要在UI线程(Android或iOS的主线程)回调.
- 数据持久化,推送,摄像头,蓝牙等,都需要平台支持
- 轻量级解决方案: 方法通道机制 Method Channel
- 调用示例:
class _MyHomePageState extends State<MyHomePage> {
//声明MethodChannel
static const platform = MethodChannel('com.xfhy.basic_ui/util');
handleButtonClick() async {
bool result;
//捕获 万一失败了呢
try {
//异步等待,可能很耗时 等待结果
result = await platform.invokeMethod('isEmpty', "have data");
} catch (e) {
result = false;
}
print('result : $result');
}
}
//Android代码
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
//参考: https://flutter.dev/docs/development/platform-integration/platform-channels
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.xfhy.basic_ui/util").setMethodCallHandler { call, result ->
//判断方法名是否支持
if (call.method == "isEmpty") {
val arguments = call.arguments
result.success(StringUtil.isEmpty(arguments as? String))
print("success")
} else {
//方法名暂不支持
result.notImplemented()
print("fail")
}
}
}
}
- Android或者iOS的数据会被序列化成一段二进制格式的数据在通道中传输,当该数据传递到Flutter后,又会被反序列化成Dart语言中的类型.