面对Flutter,我终于迈出了第一步

selectedItemColor: Color(0xff333333),
unselectedItemColor: Color(0xff999999),
selectedFontSize: 11,
unselectedFontSize: 11,
type: BottomNavigationBarType.fixed,
items: [
buildTab(“Home”, “ic_home”, “ic_home_s”),
buildTab(“Mine”, “ic_mine”, “ic_mine_s”)
]),
body: WillPopScope(
child: PageView.builder(
itemBuilder: (ctx, index) => pages[index],
controller: _controller,
physics: NeverScrollableScrollPhysics(),//禁止PageView左右滑动
),
onWillPop: () async {
if (_lastPressed == null ||
DateTime.now().difference(_lastPressed) >
Duration(seconds: 1)) {
_lastPressed = DateTime.now();
Fluttertoast.showToast(msg: “Press again to exit”);
return false;
} else {
return true;
}
}),
);
}
}

网络层封装

网络框架使用的是dio,不管是哪种平台,网络请求最终要转成实体model用于ui展示。这里先将dio做一个封装,便于使用。

通用拦截器

网络请求中通常需要添加自定义拦截器来预处理网络请求,往往需要将登录信息(如user_id等)放在公共参数中,例如;

import ‘package:dio/dio.dart’;
import ‘dart:async’;
import ‘package:shared_preferences/shared_preferences.dart’;

class CommonInterceptor extends Interceptor {
@override
Future onRequest(RequestOptions options) async {
options.queryParameters = options.queryParameters ?? {};
options.queryParameters[“app_id”] = “1001”;
var pref = await SharedPreferences.getInstance();
options.queryParameters[“user_id”] = pref.get(Constants.keyLoginUserId);
options.queryParameters[“device_id”] = pref.get(Constants.keyDeviceId);
return super.onRequest(options);
}
}

Dio封装

然后使用dio封装getpost请求,预处理响应responsecode。假设我们的响应格式是这样的:

{
code:0,
msg:“获取数据成功”,
result:[] //或者{}
}

import ‘package:dio/dio.dart’;
import ‘common_interceptor.dart’;

/*

  • 网络管理
    */
    class HttpManager {
    static HttpManager _instance;

static HttpManager getInstance() {
if (_instance == null) {
_instance = HttpManager();
}
return _instance;
}

Dio dio = Dio();

HttpManager() {
dio.options.baseUrl = “https://api.xxx.com/”;
dio.options.connectTimeout = 10000;
dio.options.receiveTimeout = 5000;
dio.interceptors.add(CommonInterceptor());
dio.interceptors.add(LogInterceptor(responseBody: true));
}

static Future<Map<String, dynamic>> get(String path, Map<String, dynamic> map) async {
var response = await getInstance().dio.get(path, queryParameters: map);
return processResponse(response);
}

/*
表单形式
*/
static Future<Map<String, dynamic>> post(String path, Map<String, dynamic> map) async {
var response = await getInstance().dio.post(path,
data: map,
options: Options(
contentType: “application/x-www-form-urlencoded”,
headers: {“Content-Type”: “application/x-www-form-urlencoded”}));
return processResponse(response);
}

static Future<Map<String, dynamic>> processResponse(Response response) async {
if (response.statusCode == 200) {
var data = response.data;
int code = data[“code”];
String msg = data[“msg”];
if (code == 0) {//请求响应成功
return data;
}
throw Exception(msg);
}
throw Exception(“server error”);
}
}

map转model

使用dio可以将最终的请求响应response转成Map<String, dynamic>对象,我们还需要将map转成相应的model。假如我们有一个获取文章列表的接口响应如下:

{
code:0,
msg:“获取数据成功”,
result:[
{
article_id:1,
article_title:“标题”,
article_link:“https://xxx.xxx”
}
]
}

就需要一个Article的model。由于Flutter下是禁用反射的,我们只能手动初始化每个成员变量。 不过我们可以通过json_serializable将手动初始化的工作交给它。 首先在pubspec.yaml引入它:

dependencies:
json_annotation: ^2.0.0

dev_dependencies:
json_serializable: ^2.0.0

我们创建一个article.dart的model类:

import ‘package:json_annotation/json_annotation.dart’;

part ‘article.g.dart’;
//FieldRename.snake 表示json字段下划线分割类型如:article_id
@JsonSerializable(fieldRename: FieldRename.snake, checked: true)
class Article {
final int articleId;
final String articleTitle;
final String articleLikn;
}

注意这里引用到了一个article.g.dart没有产生的文件,我们通过pub run build_runner build命令就会生成这个文件

// GENERATED CODE - DO NOT MODIFY BY HAND

part of ‘article.dart’;

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

Article _$ArticleFromJson(Map<String, dynamic> json) {
return $checkedNew(‘Article’, json, () {
final val = Article();
$checkedConvert(json, ‘article_id’, (v) => val.articleId = v as int);
$checkedConvert(
json, ‘article_title’, (v) => val.articleTitle = v as String);
$checkedConvert(json, ‘article_link’, (v) => val.articleLink = v as String);
return val;
}, fieldKeyMap: const {
‘articleId’: ‘article_id’,
‘articleTitle’: ‘article_title’,
‘articleLink’: ‘article_link’
});
}

Map<String, dynamic> _$ArticleToJson(Article instance) => <String, dynamic>{
‘article_id’: instance.articleId,
‘article_title’: instance.articleTitle,
‘article_link’: instance.articleLink
};

然后在article.dart里添加工厂方法

class Article{

factory Article.fromJson(Map<String, dynamic> json) => _$ArticleFromJson(json);
}

具体请求封装

创建好model类后,就可以建一个具体的api请求类ApiRepository,通过async库,可以将网络请求最终封装成一个Future对象,实际调用时,我们可以将异步回调形式的请求转成同步的形式,这有点和kotlin的协程类似:

import ‘dart:async’;
import ‘…/http/http_manager.dart’;
import ‘…/model/article.dart’;

class ApiRepository {
static Future<List

> articleList() async {
var data = await HttpManager.get(“articleList”, {“page”: 1});
return data[“result”].map((Map<String, dynamic> json) {
return Article.fromJson(json);
});
}
}

实际调用

封装好网络请求后,就可以在具体的组件中使用了。假设有一个_ArticlePageState

import ‘package:flutter/material.dart’;
import ‘…/model/article.dart’;
import ‘…/repository/api_repository.dart’;

class ArticlePage extends StatefulWidget {
@override
State createState() {
return _ArticlePageState();
}
}

class _ArticlePageState extends State {
List

_list = [];

@override
void initState() {
super.initState();
_loadData();
}

void _loadData() async {//如果需要展示进度条,就必须try/catch捕获请求异常。
showLoading();
try {
var list = await ApiRepository.articleList();
setState(() {
_list = list;
});
} catch (e) {}
hideLoading();
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ListView.builder(
itemCount: _list.length,
itemBuilder: (ctx, index) {
return Text(_list[index].articleTitle);
})),
);
}
}

数据库

数据库操作通过sqflite,简单封装处理事例了文章Article的插入操作。

import ‘package:sqflite/sqflite.dart’;
import ‘package:path/path.dart’;
import ‘dart:async’;
import ‘…/model/article.dart’;

class DBManager {
static const int _VSERION = 1;
static const String _DB_NAME = “database.db”;
static Database _db;
static const String TABLE_NAME = “t_article”;
static const String createTableSql = ‘’’
create table $TABLE_NAME(
article_id int,
article_title text,
article_link text,
user_id int,
primary key(article_id,user_id)
);
‘’';

static init() async {
String dbPath = await getDatabasesPath();
String path = join(dbPath, _DB_NAME);
_db = await openDatabase(path, version: _VSERION, onCreate: _onCreate);
}

static _onCreate(Database db, int newVersion) async {
await db.execute(createTableSql);
}

static Future insertArticle(Article item, int userId) async {
var map = item.toMap();
map[“user_id”] = userId;
return _db.insert(“$TABLE_NAME”, map);
}
}

Android层兼容通信处理

为了兼容底层,需要通过MethodChannel进行FlutterNative(Android/iOS)通信

flutter调用Android层方法

这里举例flutter端打开系统相册意图,并取得最终的相册路径回调给flutter端。 我们在Android中的MainActivityonCreate方法处理通信逻辑

eventChannel = MethodChannel(flutterView, “event”)
eventChannel?.setMethodCallHandler { methodCall, result ->
when (methodCall.method) {
“openPicture” -> PictureUtil.openPicture(this) {
result.success(it)
}
}
}

因为是通过result.success将结果回调给Flutter端,所以封装了打开相册的工具类。

object PictureUtil {
fun openPicture(activity: Activity, callback: (String?) -> Unit) {
val f = getFragment(activity)
f.callback = callback
val intentToPickPic = Intent(Intent.ACTION_PICK, null)
intentToPickPic.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, “image/*”)
f.startActivityForResult(intentToPickPic, 200)
}

private fun getFragment(activity: Activity): PictureFragment {
var fragment = activity.fragmentManager.findFragmentByTag(“picture”)
if (fragment is PictureFragment) {

} else {
fragment = PictureFragment()
activity.fragmentManager.apply {
beginTransaction().add(fragment, “picture”).commitAllowingStateLoss()
executePendingTransactions()
}
}
return fragment
}
}

然后在PictureFragment中加入callback,并且处理onActivityResult逻辑

结语

网上高级工程师面试相关文章鱼龙混杂,要么一堆内容,要么内容质量太浅, 鉴于此我整理了上述安卓开发高级工程师面试题以及答案。希望帮助大家顺利进阶为高级工程师。
目前我就职于某大厂安卓高级工程师职位,在当下大环境下也想为安卓工程师出一份力,通过我的技术经验整理了面试经常问的题,答案部分是一篇文章或者几篇文章,都是我认真看过并且觉得不错才整理出来。

大家知道高级工程师不会像刚入门那样被问的问题一句话两句话就能表述清楚,所以我通过过滤好文章来帮助大家理解。

1307页字节跳动Android面试真题解析火爆全网,完整版开放下载

现在都说互联网寒冬,其实只要自身技术能力够强,咱们就不怕!我这边专门针对Android开发工程师整理了一套【Android进阶学习视频】、【全套Android面试秘籍】、【Android知识点PDF】。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
,在当下大环境下也想为安卓工程师出一份力,通过我的技术经验整理了面试经常问的题,答案部分是一篇文章或者几篇文章,都是我认真看过并且觉得不错才整理出来。

大家知道高级工程师不会像刚入门那样被问的问题一句话两句话就能表述清楚,所以我通过过滤好文章来帮助大家理解。

[外链图片转存中…(img-ZZOgKbwO-1715163028764)]

现在都说互联网寒冬,其实只要自身技术能力够强,咱们就不怕!我这边专门针对Android开发工程师整理了一套【Android进阶学习视频】、【全套Android面试秘籍】、【Android知识点PDF】。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值