框架
之前做 APP 开发的时候,我都是负责 Android 组 leader,新我的项目起来,我会做技术预研,如《一套残缺的 Android 通用框架》,个别会应用 MVP 模式(当初应该是 MVVM 模式),网络申请框架应用 Retrofit,图片加载应用 Glide,图片缩放和裁剪别离应用 PhotoView 和 uCrop 等,必要时,我会写个 sample 放我的项目里,让共事能够参考。
这个也是个新我的项目,我也须要做下技术预研,Flutter 网络申请框架须要应用什么?图片加载又应用什么?文章详情,我打算应用 Markdown,这 Flutter 能实现吗?等等,这些都是须要事先做好调研。
这个我的项目,代码版本治理用 GitHub,首先新建一个 Flutter 我的项目,GitHub 也新建个公有我的项目(临时不公开吧),用如下命令将本地代码和近程 GitHub 关联起来。
echo "# andblog" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin https://github.com/WuXiaolong/andblog.git
git push -u origin master
关联 OK,前面批改,就间接应用 Android Studio 自带的 Git 来提交代码。
接下来来看看 Flutter 网络申请框架应用什么?怎么应用?
网络
数据起源
说到网络申请框架,首先要解决数据从何而来,我没有后端(其实我能够开发),没有服务器,怎么搞?莫急,都说本系列文章是从零开发 APP,且能一个人做一个我的项目,我天然有方法。
数据我应用的 Bmob,它能够创立你想要的表,反对 RestAPI,这能够为做 APP 省去后端开发成本,当然像 Bmob 提供这样的服务有很多,就不一一介绍,Bmob 如何应用,也不说了,官网有很具体的文档,你能够点击文章底部「浏览原文」注册个账号玩玩。
http
网络申请框架的数据有了,能够玩起来了。
以申请文章列表接口示例,先用 Postman 看下数据结构:
{
"results": [
{
"content": "文章内容测试 1",
"cover": "http://pic1.win4000.com/wallpaper/2020-04-21/5e9e676001e20.jpg",
"createdAt": "2020-07-05 13:50:58",
"date": "2020.07.01",
"objectId": "ct7BGGGV",
"summary": "摘要 1",
"title": "题目测试 1",
"updatedAt": "2020-07-05 13:53:16"
},
{
"content": "文章内容测试 2",
"cover": "http://pic1.win4000.com/wallpaper/2020-04-21/5e9e676001e20.jpg",
"createdAt": "2020-07-05 13:52:37",
"date": "2020.07.02",
"objectId": "3L42777G",
"summary": "摘要 2",
"title": "题目测试 2",
"updatedAt": "2020-07-05 13:53:10"
}
]
}
Flutter 提供了网络申请框架是 http,地址:https://pub.flutter-io.cn/pac…
增加 http 包,在 pubspec.yaml 增加:
dependencies:
http: ^0.12.1
我的项目根目录执行命令 flutter pub get
装置软件包。
新建 blog_list_page.dart 用来展现文章列表,blog.dart 是文章列表的构造表,把入口 main.dart 间接加载文章列表,具体代码如下。
main.dart:
import 'package:flutter/material.dart';
import 'andblog/list/blog_list_page.dart';
void main() {runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'AndBlog',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: new BlogListPage(),);
}
}
blog_list_page.dart:
import 'package:flutter/material.dart';
import 'package:flutter_andblog/andblog/http/http_common.dart';
import 'package:http/http.dart' as http;
import 'blog.dart';
class BlogListPage extends StatefulWidget {
@override
BlogListPageState createState() => new BlogListPageState();
}
class BlogListPageState extends State<BlogListPage> {List<Blog> blogList = [];
@override
void initState() {super.initState();
// 一进页面就申请接口
getBlogListData();}
// 网络申请
getBlogListData() async {var response = await http.get(HttpCommon.blog_list_url, headers: HttpCommon.headers());
if (response.statusCode == 200) {
// setState 相当于 runOnUiThread
setState(() {blogList = Blog.decodeData(response.body);
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('AndBlog'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:',),
],
),
),
floatingActionButton: FloatingActionButton(
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
把网络 url 都放在 HttpCommon,具体代码在 http_common.dart:
class HttpCommon{
static var blog_list_url = 'https://api2.bmob.cn/1/classes/ArticleTable/';
static Map<String, String> headers(){
// 设置 header
Map<String, String> headers = new Map();
headers["X-Bmob-Application-Id"] = "bmob Application-Id";
headers["X-Bmob-REST-API-Key"] = "bmob REST-API-Key";
headers["Content-Type"] = "application/json";
return headers;
}
}
网络申请数据解析放在 blog.dart:
import 'dart:convert';
class Blog{
final String content;
final String cover;
final String date;
final String objectId;
final String summary;
final String title;
// 构造函数
Blog({
this.content,
this.cover,
this.date,
this.objectId,
this.summary,
this.title,
});
static List<Blog> decodeData(String jsonData) {List<Blog> blogList = new List<Blog>();
var data = json.decode(jsonData);
var results = data['results'];
print('results='+results[0]['content']);
for (int i = 0; i < results.length; i++) {blogList.add(fromMap(results[i]));
}
return blogList;
}
static Blog fromMap(Map<String, dynamic> map) {
return new Blog(content: map['content'],
cover: map['cover'],
date: map['date'],
objectId: map['objectId'],
summary: map['summary'],
title: map['title'],
);
}
}
我习惯性打印 print('results='+results[0]['content']);
看看数据解析对不对,屡次尝试最初打印 文章内容测试 1
,达到了预期。
json_serializable
在写文章列表的构造 blog.dart 须要手动一个个敲字段,而后解析,Flutter 有没有像 GsonFormat 这样主动解析的插件,当然是有,是 json_serializable,应用 json_serializable,你须要一个惯例依赖,以及两个 dev 依赖:
dependencies:
flutter:
sdk: flutter
json_annotation: ^3.0.1
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^1.10.0
json_serializable: ^3.3.0
我的项目根目录执行命令 flutter pub get
装置软件包。
以文章详情构造体示例:
{
"content": "文章内容测试 1",
"cover": "http://pic1.win4000.com/wallpaper/2020-04-21/5e9e676001e20.jpg",
"createdAt": "2020-07-05 13:50:58",
"date": "2020.07.01",
"objectId": "ct7BGGGV",
"summary": "摘要 1",
"title": "题目测试 1",
"updatedAt": "2020-07-05 13:53:16"
}
依据 json 创立实体类 detail.dart:
import 'package:json_annotation/json_annotation.dart';
// 为了使实体类文件找到生成文件,须要 part 'detail.g.dart'
part 'detail.g.dart';
@JsonSerializable()
class Detail{
final String content;
final String cover;
final String date;
final String objectId;
final String summary;
final String title;
// 构造函数
Detail({
this.content,
this.cover,
this.date,
this.objectId,
this.summary,
this.title,
});
}
刚写完 detail.g.dart 会报错,这是失常的!因为咱们还没生成解析文件。
接下来解析,我的项目根目录执行命令flutter packages pub run build_runner build
会发现生成一个 detail.g.dart 文件:
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'detail.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Detail _$DetailFromJson(Map<String, dynamic> json) {
return Detail(content: json['content'] as String,
cover: json['cover'] as String,
date: json['date'] as String,
objectId: json['objectId'] as String,
summary: json['summary'] as String,
title: json['title'] as String,
);
}
Map<String, dynamic> _$DetailToJson(Detail instance) => <String, dynamic>{
'content': instance.content,
'cover': instance.cover,
'date': instance.date,
'objectId': instance.objectId,
'summary': instance.summary,
'title': instance.title,
};
而后把这两个办法放到 detail.dart:
factory Detail.fromJson(Map<String, dynamic> json) => _$DetailFromJson(json);
Map<String, dynamic> toJson() => _$DetailToJson(this);
接下来就能够调用 fromJson 办法解析网络申请的数据:
var data = json.decode(response.body);
detail = Detail.fromJson(data);
print('results='+detail.title);
这样看下来,应用 json_serializable 并没有不便多少,只是把解析字段省了,最烦没有把增加字段步骤自动化,比 GsonFormat 弱爆了。