一个Android菜鸟入门Flutter 笔记(二)

void getRequest() async {

//创建网络调用示例

Dio dio = new Dio();

//设置URI及请求user-agent后发起请求

var response = await dio.get(“https://flutter.dev”, options:Options(headers: {“user-agent” : “Custom-UA”}));

//打印请求结果

if(response.statusCode == HttpStatus.ok) {

print(response.data.toString());

} else {

print(“Error: ${response.statusCode}”);

}

}

//下载-------------

//使用FormData表单构建待上传文件

FormData formData = FormData.from({

“file1”: UploadFileInfo(File(“./file1.txt”), “file1.txt”),

“file2”: UploadFileInfo(File(“./file2.txt”), “file1.txt”),

});

//通过post方法发送至服务端

var responseY = await dio.post(“https://xxx.com/upload”, data: formData);

print(responseY.toString());

//使用download方法下载文件

dio.download(“https://xxx.com/file1”, “xx1.zip”);

//增加下载进度回调函数

dio.download(“https://xxx.com/file1”, “xx2.zip”, onReceiveProgress: (count, total) {

//do something

});

//并行请求--------------

//同时发起两个并行请求

List responseX= await Future.wait([dio.get(“https://flutter.dev”),dio.get(“https://pub.dev/packages/dio”)]);

//打印请求1响应结果

print(“Response1: ${responseX[0].toString()}”);

//打印请求2响应结果

print(“Response2: ${responseX[1].toString()}”);

//拦截器-----------------

//增加拦截器

dio.interceptors.add(InterceptorsWrapper(

onRequest: (RequestOptions options){

//为每个请求头都增加user-agent

options.headers[“user-agent”] = “Custom-UA”;

//检查是否有token,没有则直接报错

if(options.headers[‘token’] == null) {

return dio.reject(“Error:请先登录”);

}

//检查缓存是否有数据

if(options.uri == Uri.parse(‘http://xxx.com/file1’)) {

return dio.resolve(“返回缓存数据”);

}

//放行请求

return options;

}

));

//增加try catch,防止请求报错

try {

var response = await dio.get(“https://xxx.com/xxx.zip”);

print(response.data.toString());

}catch(e) {

print(e);

}

2.JSON解析


  • 只能手动解析.

import ‘dart:convert’;

String jsonString = ‘’’

{

“id”:“123”,

“name”:“张三”,

“score” : 95,

“teacher”: { “name”: “李四”, “age” : 40 }

}

‘’';

//json解析

//所谓手动解析,是指使用 dart:convert 库中内置的 JSON 解码器,将 JSON 字符串解析成自定义对象的过程。

class Teacher {

String name;

int age;

Teacher({this.name, this.age});

factory Teacher.fromJson(Map<String, dynamic> parsedJson) {

return Teacher(name: parsedJson[‘name’], age: parsedJson[‘age’]);

}

@override

String toString() {

return ‘Teacher{name: $name, age: $age}’;

}

}

class Student {

String id;

String name;

int score;

Teacher teacher;

Student({this.id, this.name, this.score, this.teacher});

//从Map中取

factory Student.fromJson(Map<String, dynamic> parsedJson) {

return Student(

id: parsedJson[‘id’],

name: parsedJson[‘name’],

score: parsedJson[‘score’],

teacher: Teacher.fromJson(parsedJson[‘teacher’]));

}

@override

String toString() {

return ‘Student{id: $id, name: $name, score: $score, teacher: $teacher}’;

}

}

void main() {

final jsonResponse = json.decode(jsonString);//将字符串解码成Map对象

Student student = Student.fromJson(jsonResponse);//手动解析

print(student.teacher.name);

}

  • json解析比较耗时,放compute中去进行,不用担心阻塞UI了. compute得有Widget才行.

3. 数据持久化


  • 由于 Flutter 仅接管了渲染层,真正涉及到存储等操作系统底层行为时,还需要依托于原生 Android、iOS.

  • 三种数据持久化方法,即文件、SharedPreferences 与数据库

  • Flutter 提供了两种文件存储的目录,即临时(Temporary)目录与文档(Documents)目录:

3.1 文件

需要引入: path_provider: ^1.6.4

//创建文件目录

Future get _localFile async {

final directory = await getApplicationDocumentsDirectory();

final path = directory.path;

return File(‘$path/content.txt’);

}

//将字符串写入文件

Future writeContent(String content) async {

final file = await _localFile;

return file.writeAsString(content);

}

//从文件读出字符串

Future readContent() async {

try {

final file = await _localFile;

String contents = await file.readAsString();

return contents;

} catch (e) {

return “”;

}

}

3.2 SharedPreferences

需要引入: shared_preferences: ^0.5.6+2

//读取SharedPreferences中key为counter的值

Future_loadCounter() async {

SharedPreferences prefs = await SharedPreferences.getInstance();

int counter = (prefs.getInt(‘counter’) ?? 0);

return counter;

}

//递增写入SharedPreferences中key为counter的值

Future_incrementCounter() async {

SharedPreferences prefs = await SharedPreferences.getInstance();

int counter = (prefs.getInt(‘counter’) ?? 0) + 1;

prefs.setInt(‘counter’, counter);

}

3.3 数据库

需要引入: sqflite: ^1.2.1

dbDemo() async {

final Future 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 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> 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();

}

4. 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 {

//声明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语言中的类型.

5. Flutter中复用原生控件


  • 除去地图、WebView、相机等涉及底层方案的特殊情况外,大部分原生代码能够实现的 UI 效果,完全可以用 Flutter 实现.

  • 使用这种方式对性能造成非常大的影响且不方便维护.

  • 方法通道: 原生逻辑复用

  • 平台视图: 原生视图复用

6. Android项目中嵌入Flutter


官网地址: https://flutter.dev/docs/development/add-to-app

  • FlutterEngine 文档: https://github.com/flutter/flutter/wiki/Experimental:-Reuse-FlutterEngine-across-screens

  • FlutterView 文档: https://github.com/flutter/flutter/wiki/Experimental:-Add-Flutter-View

  • API一会儿就过时了,得去官网看最新的才行.

  • 可以在Android App中开启Flutter的Activity,Flutter的Activity是在另外一个进程,第一次进入特别慢.也可以加入Flutter的View和Fragment

  • 在Android工程下新建一个Flutter的module比较简单直接

7. 混合开发导航栈


  • Android跳转Flutter,依赖FlutterView.Flutter在FlutterView中建立了自己的导航栈.

  • 通常会将Flutter容器封装成一个独立的Activity或者ViewController. 这样打开一个普通的Activity既是打开Flutter界面了

  • Flutter页面跳转原生界面,需要利用方法通道,然后用原生去打开响应的界面.

  • Flutter实例化成本非常高,每启动一个Flutter实例,就会创建一套新的渲染机制,即Flutter Engine,以及底层的Isolate.而这些实例之间的内存是不相互共享的,会带来较大的系统资源消耗.

  • 实际开发中,尽量用Flutter去开发闭环的业务模块.原生跳转过去就行,剩下的全部由Flutter内部完成. 尽量避免Flutter页面回到原生页面,原生页面又启动新的Flutter实例的情况.

8. 状态管理(跨组件传递数据,Provider)


  • Dart的一个库,可以实现在StatelessWidget中刷新数据.跨组件传递数据.全局共享数据.依赖注入

  • 使用Provider后,我们就再也不需要StalefullWidget了.

  • Provider以InheritedWidget语法糖的方法,通过数据资源封装,数据注入,和数据读写这3个步骤,为我们实现了跨组件(跨页面)之间的数据共享

  • 我们既可以用Provider来实现静态的数据读传递,也可以使用ChangeNotifierProvider来实现动态的数据读写传递,还用通过MultiProvider来实现多个数据资源的共享

  • Provider.of和Consumer都可以实现数据的读取,并且Consumer还可以控制UI刷新的粒度,避免与数据无关的组件的无谓刷新

  • 封装数据

//定义需要共享的数据模型,通过混入ChangeNotifier管理听众

class CounterModel with ChangeNotifier {

int _count = 0;

//读方法

int get counter => _count;

//写方法

void increment() {

_count++;

notifyListeners();//通知听众刷新

}

}

  • 放数据

尽量把数据放到更高的层级

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

//通过Provider组件封装数据资源

//因Provider是InheritedWidget的语法糖,所以它是一个Widget

//ChangeNotifierProvider只能搞一个

//MultiProvider可以搞多个

return MultiProvider(

providers: [

//注入字体大小 下个界面读出来

Provider.value(value: 30.0),

//注入计数器实例

ChangeNotifierProvider.value(value: CounterModel())

],

child: MaterialApp(

home: FirstPage(),

),

);

}

}

  • 读数据

//示例: 读数据

class FirstPage extends StatelessWidget {

@override

Widget build(BuildContext context) {

//取出资源 类型是CounterModel

//获取计时器实例

final _counter = Provider.of(context);

//获取字体大小

final textSize = Provider.of(context);

/*

//使用Consumer2获取两个数据资源

Consumer2<CounterModel,double>(

//builder函数以参数的形式提供了数据资源

builder: (context, CounterModel counter, double textSize, _) => Text(

‘Value: ${counter.counter}’,

style: TextStyle(fontSize: textSize))

更多学习和讨论,欢迎加入我们!

有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

这里有2000+小伙伴,让你的学习不寂寞~·

fierProvider.value(value: CounterModel())

],

child: MaterialApp(

home: FirstPage(),

),

);

}

}

  • 读数据

//示例: 读数据

class FirstPage extends StatelessWidget {

@override

Widget build(BuildContext context) {

//取出资源 类型是CounterModel

//获取计时器实例

final _counter = Provider.of(context);

//获取字体大小

final textSize = Provider.of(context);

/*

//使用Consumer2获取两个数据资源

Consumer2<CounterModel,double>(

//builder函数以参数的形式提供了数据资源

builder: (context, CounterModel counter, double textSize, _) => Text(

‘Value: ${counter.counter}’,

style: TextStyle(fontSize: textSize))

[外链图片转存中…(img-m76y86Uw-1720112674668)]

更多学习和讨论,欢迎加入我们!

有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

这里有2000+小伙伴,让你的学习不寂寞~·

  • 15
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值