第一章 学习指南
1、AS 快捷键
快捷创建Widget: 在dart文件中输入stf或stl出现提示后按回车即可
快捷修复: option + 回车
自动生成构造函数: 选中 final 参数, 快捷键: option + 回车
添加父组件、变为子组件、删除子组件: option + 回车
万能的搜索: 双击shift
查看最近打开的文件: command + E
重命名: fn + shift + f6
查看当前类结构: command + fn + f12
查看源码: 将光标放到要查看源码的类名或方法名上, 长按command然后的点击
查看类的子类: 选中要查看的类, 然后: command + B 或 option + command + B
将代码更新到模拟器上: 选中模拟器然后 command + R
导入类的快捷键: 将光标放在要导入类的上面, 然后按 option + enter
前进后退: 当跟踪代码的时候, 经常跳转到其他类, 后退快捷键: option + command+ 方向左键, 前进快捷键: option + command+ 方向右键
全局搜索: command + shift + F
全局替换: command + shift + R
查找引用: option + shift + F7
Cmd + Option + L : 代码格式化。
Cmd + “-” 或者 “+” :收起和展开代码。
Cmd + Shift + “-” 或者 “+” : 收起和展开全部代码。
Cmd + Shift + Enter :代码快速补全。
Cmd + F12 :快速查看当前文件所有方法。
Cmd + Shift + F :全局搜索。
Cmd + Shift + R :全局替换。
Cmd + F :当前文件搜索。
Cmd + R :当前文件替换。
Cmd + Option + M :方法抽离或重构。
Cmd + Option + V : 抽离局部变量。
Cmd + Option + F :抽离成员变量。
Cmd + O :全局搜索类/文件/关键字/(包括系统类,自定义类)。
Cmd + Option + W :抽取代码为单独的方法,或者抽取成get方法。
Cmd + Option + E :创建文件,抽取代码为单独的方法,或者抽取成get方法。
Cmd + Option + B :查看抽象类的实现。
Cmd + D :复制单行。
Cmd+ Delete :删除行。
Cmd + \ :热重载(hot reload) 。
Cmd + Option + \ :热重启(hot restart)。
Cmd+ Shift + Enter :if后面自动加(){ }。
Cmd+ J :快速生成模版代码块。
Ctl+ R :运行项目。
Option + Enter :在widget包裹一个新的widget。
Option + Enter :自动修正错误(但是要把光标移动到错误上面)。
Option + Enter :将StatelessWidget转 StatefulWidget。
Option + Up :选择这个widget。
Option + Ctl + O :清除无效包引用。
Option + Ctl + I :自动缩进对齐/代码对齐。
Option + Shift + Up/Down :上下移动代码。
Option + 双击 Up :选择区域。
选中代码 + tab :选中代码缩进。
选中代码 + shift + tab :选中代码缩进。
2、自动补全
可以在Android Studio的Plugin中搜素 Flutter Snippets这个插件然后进行安装
3、代码提取
选中代码-->右键选择"Refactor"-->Extract Method...(提取成方法)
4、代码自动格式化
虽然我们可以通过快捷键option(alt)+command(ctrl)+L来在提交代码时格式化, 但是这种手动方式显然还不够效率. 下面我们来借助AS的保存时代码自动格式化功能来释放双手: 在Settings-->Language&Frameworks-->Flutter中选上"Format Code on Save"这个选项. (保存代码时自动格式化)
第二章 Flutter网络和数据存储框架搭建
1、 Flutter网络架构架构设计
支持网络库插拔设计, 且不干扰业务层
简洁易用, 支持配置来进行请求
Adapter设计, 扩展性强
统一异常和返回处理
https://github.com/szy0syz/flutter_bilibili
HitNet | |
---|---|
Dao层 | 首页、详情、点赞、收藏、登录、注册、通知、个人中心、排行... |
REST ful支持 | GET、POST、DELETE |
统一逻辑处理 | Request Processing、Response Processing、Error Processing |
适配层 | Dio Adapter、Http Adapter、Mock Adapter |
三方库 | dio、http、Mock |
2、基于配置的请求封装与hinet架构搭建
在lib目录下, 新建http目录; 在http目录下分别创建core和request目录; 在request目录下新建"base_request.dart"文件, 在base_request.dart中:
enum HttpMethod { GET, POST, DELETE }
///抽象类封装--基础请求
abstract class BaseRequest {
// curl -X GET "http://api.devio.org/uapi/test/test?requestPrams=11" -H "accept: */*"
// curl -X GET "https://api.devio.org/uapi/test/test/1
var pathParams;
var useHttps = true; // 用于切换http和https
String authority() {//设置域名
return "api.devio.org";
}
HttpMethod httpMethod();
String path();
String url() {
Uri uri;
var pathStr = path();
//拼接path参数
if (pathParams != null) {
if (path().endsWith("/")) {
pathStr = "${path()}$pathParams";
} else {
pathStr = "${path()}/$pathParams";
}
}
//http和https切换
if (useHttps) {
uri = Uri.https(authority(), pathStr, params);
} else {
uri = Uri.http(authority(), pathStr, params);
}
if (needLogin()) {
//给需要登录的接口携带登录令牌
addHeader(LoginDao.BOARDING_PASS, LoginDao.getBoardingPass());
}
print('url:${uri.toString()}');
return uri.toString();
}
bool needLogin();// 用于判断是否需要登录
Map params = Map();
///添加参数
BaseRequest add(String k, Object v) {
params[k] = v.toString();
return this;
}
Map header = {
HiConstants.authTokenK: HiConstants.authTokenV,
HiConstants.courseFlagK: HiConstants.courseFlagV,
// 'course-flag': 'fa',
// "auth-token": "ZmEtMjAyMS0wNC0xMiAyMToyMjoyMC1mYQ==fa",
};
///添加header
BaseRequest addHeader(String k, Object v) {
header[k] = v.toString();
return this;
}
}
3、基于配置的请求封装与hinet架构搭建-2
在http目录-->request目录--> 新建 test_request.dart 文件, 用于测试接口:
import 'package:flutter_bilibili/http/request/base_request.dart';
//测试排程类
class TestRequest extends BaseRequest {
@override
HttpMethod httpMethod() {
return HttpMethod.GET;
}
@override
bool needLogin() {
return false;
}
@override
String path() {
return 'uapi/test/test';
}
}
在http目录-->core目录-->新建 hi_net.dart 文件:
import 'package:flutter_bilibili/http/core/dio_adapter.dart';
import '../request/base_request.dart';
import 'hi_error.dart';
import 'hi_net_adapter.dart';
///1.支持网络库插拔设计,且不干扰业务层
///2.基于配置请求请求,简洁易用
///3.Adapter设计,扩展性强
///4.统一异常和返回处理
class HiNet {
HiNet._();
static HiNet? _instance;//创建单例
static HiNet getInstance() {//静态方法获取单例
if (_instance == null) {
_instance = HiNet._();
}
return _instance!;
}
// 获取请求结果
Future fire(BaseRequest request) async {
HiNetResponse? response;
var error;
try {
response = await send(request);
} on HiNetError catch (e) {
error = e;
response = e.data;
printLog(e.message);
} catch (e) {
//其它异常
error = e;
printLog(e);
}
if (response == null) {
printLog(error);
}
var result = response?.data;
printLog(result);
var status = response?.statusCode;
switch (status) {
case 200:
return result;
case 401:
throw NeedLogin();
case 403:
throw NeedAuth(result.toString(), data: result);
default:
throw HiNetError(status ?? -1, result.toString(), data: result);
}
}
//发送请求
Future> send(BaseRequest request) async {
///使用Dio发送请求
HiNetAdapter adapter = DioAdapter();
return adapter.send(request);
}
void printLog(log) {//用于测试打印
print('hi_net:' + log.toString());
}
}
在随便一个文件内, 构建测试请求:
void _incrementCounter() async {
TestRequest request = TestRequest();
request.add("aa", "ddd").add("bb", "333");
var result = await HiNet.getInstance().fire(request);
print(result);
}
4、 hinet统一异常和返回处理与Adapter模式设计
在http-->core-->新建 hi_error.dart, 用来统一处理异常:
///需要登录的异常
class NeedLogin extends HiNetError {
NeedLogin({int code: 401, String message: '请先登录'}) : super(code, message);
}
///需要授权的异常
class NeedAuth extends HiNetError {
NeedAuth(String message, {int code: 403, dynamic data})
: super(code, message, data: data);
}
///网络异常统一格式类
class HiNetError implements Exception {
final int code;
final String message;
final dynamic data;
HiNetError(this.code, this.message, {this.data});
}
在http-->core-->新建 hi_net_adapter.dart, 用来统一处理网络层返回格式:
import 'dart:convert';
import 'package:flutter_bilibili/http/request/base_request.dart';
///网络请求抽象类
abstract class HiNetAdapter {
Future> send(BaseRequest request);
}
/// 统一网络层返回格式
class HiNetResponse {
// 构造方法: 非必填
HiNetResponse({
this.data,
required this.request,
this.statusCode,
this.statusMessage,
this.extra,
});
/// Response body. may have been transformed, please refer to [ResponseType].
T? data;
/// The corresponding request info.
BaseRequest request;
/// Http status code.
int? statusCode;
/// Returns the reason phrase associated with the status code.
/// The reason phrase must be set before the body is written
/// to. Setting the reason phrase after writing to the body.
String? statusMessage;
/// Custom field that you can retrieve it later in `then`.
late dynamic extra;
/// We are more concerned about `data` field.
@override
String toString() {
if (data is Map) {
return json.encode(data);
}
return data.toString();
}
}
在http-->core-->新建 mock_adapter.dart, 用来新建测试适配器:
import 'package:flutter_bilibili/http/core/hi_net_adapter.dart';
import 'package:flutter_bilibili/http/request/base_request.dart';
///测试适配器,mock数据
class MockAdapter extends HiNetAdapter {
@override
Future> send(BaseRequest request) async {
return Future.delayed(Duration(milliseconds: 1000), () {
return HiNetResponse(
request: request,
data: {"code": 0, "message": 'success'} as T,
statusCode: 403);
});
}
}
在hi_net.dart中使用:
//发送请求
Future> send(BaseRequest request) async {
///使用Dio发送请求
HiNetAdapter adapter = MockAdapter();
return adapter.send(request);
}
5、扩展hinet添加对dio的支持
在pubspec.yaml中导入dio三方库:
dependencies:
flutter:
sdk: flutter
...
cupertino_icons: ^1.0.2
dio: ^4.0.0
在http-->core-->新建 dio_adapter.dart ,用于适配Dio:
import 'package:dio/dio.dart';
import 'package:flutter_bilibili/http/core/hi_error.dart';
import 'package:flutter_bilibili/http/core/hi_net_adapter.dart';
import 'package:flutter_bilibili/http/request/base_request.dart';
///Dio适配器
class DioAdapter extends HiNetAdapter {
@override
Future> send(BaseRequest request) async {
// 搞两个变量,一个没初始化赋值,一个初始化赋值
// options 是Dio的可选参数
var response, options = Options(headers: request.header);
var error;
try { //发送网络请求
if (request.httpMethod() == HttpMethod.GET) {
response = await Dio().get(request.url(), options: options);
} else if (request.httpMethod() == HttpMethod.POST) {
response = await Dio()
.post(request.url(), data: request.params, options: options);
} else if (request.httpMethod() == HttpMethod.DELETE) {
response = await Dio()
.delete(request.url(), data: request.params, options: options);
}
} on DioError catch (e) {
error = e;
response = e.response;
}
if (error != null) {
///抛出HiNetError
throw HiNetError(response?.statusCode ?? -1, error.toString(),
data: await buildRes(response, request));
}
return buildRes(response, request);
}
///构建HiNetResponse
Future> buildRes(
Response? response, BaseRequest request) {
return Future.value(HiNetResponse(
//?.防止response为空
data: response?.data,
request: request,
statusCode: response?.statusCode,
statusMessage: response?.statusMessage,
extra: response));
}
}
6、Dart JSON编码器和解码器剖析
void test(){
const jsonString = "{\"name\":\"flutter\",}"
//json 转map
Map jsonMap = jsonDecode(jsonString);
print('name:${jsonMap['name']}');
//map 转JSON
String json = jsonEncode(jsonMap);
print('json:$json');
}
7、三种JSON解析技巧带你解放生产力
手写实体类
-
网页自动生成实体类✅
通过: https://www.devio.org/io/tools/json-to-dart/ 在线生成实体类
-
json_serializable使用技巧
官方demo: https://github.com/google/json_serializable.dart/tree/master/example
Step1: 安装插件
dependencies:
...
dio: ^4.0.0
json_annotation: ^4.0.1
dev_dependencies:
...
json_serializable: ^4.1.3
build_runner: ^2.0.4
Step2: 配置实体类
示例: 比如在result.dart中:
// result.g.dart 将在我们运行生成命令后自动生成
import 'package:json_annotation/json_annotation.dart';
part 'result.g.dart'; //运行命令后自动生成的文件名
@JsonSerializable()
class Result {
//定义字段
int code;
String method;
String requestPrams;
//构造方法
Result(this.code, this.method, this.requestPrams);
//固定格式,不同的类使用不同的mixin即可
factory Result.fromJson(Map json) => _$ResultFromJson(json);
//固定格式,
Map toJson() => _$ResultToJson(this);
}
因为实体类的生成代码还不存在, 所以上面代码会提示一些错误是正常现象
Step3 : 执行build生成实体类
$ flutter packages pub run build_runner build
11 统一缓存管理框架hicache设计
添加依赖插件: shared_preferences
shared_preferences: ^0.5.12+4
hi_cache封装
import 'package:shared_preferences/shared_preferences.dart';
///缓存管理类
class HiCache {
SharedPreferences? prefs;
HiCache._() {
init();
}
static HiCache? _instance;
HiCache._pre(SharedPreferences prefs) {
this.prefs = prefs;
}
///预初始化,防止在使用get时,prefs还未完成初始化
static Future preInit() async {
if (_instance == null) {
var prefs = await SharedPreferences.getInstance();
_instance = HiCache._pre(prefs);
}
return _instance!;
}
static HiCache getInstance() {
if (_instance == null) {
_instance = HiCache._();
}
return _instance!;
}
//初始化
void init() async {
if (prefs == null) {
prefs = await SharedPreferences.getInstance();
}
}
setString(String key, String value) {
prefs?.setString(key, value);
}
setDouble(String key, double value) {
prefs?.setDouble(key, value);
}
setInt(String key, int value) {
prefs?.setInt(key, value);
}
setBool(String key, bool value) {
prefs?.setBool(key, value);
}
setStringList(String key, List value) {
prefs?.setStringList(key, value);
}
remove(String key) {
prefs?.remove(key);
}
// 通过泛型的方式来获取数据, 提高复用性
T? get(String key) {
var result = prefs?.get(key);
if (result != null) {
return result as T;
}
return null;
}
}
测试使用封装的hi_cache
在main.dart中:
class _MyHomePageState extends State{
@voerride
void initState() {
super.initState();
HiCache.preInit(); //初始化
}
}
/// 保存与读取本地信息
void test(){
HiCache.getInstance().setString("aa","1234");
var value = HiCache.getInstance().get("aa");
print('value:$value')
}