摘自:https://www.cnblogs.com/fengqingyangNo1/p/12927538.html
简介
最近花了两天时间研究使用Flutter开发一个抖音国际版. 个人感觉使用Flutter开发app快得不要不要的额. 两天就基本可以开发个大概出来. 最主要是热重载,太方便实时调整UI布局了. 相应速度极快. 如下图:
主要项目架构
详细说明一下,开发主要在lib文件夹
- pubspec.yaml是配置插件的位置,如http: ^0.12.0+4,类似依赖组件.
- common文件夹存放的是重写的网络组件,以及图标组件icons.dart
- config文件夹存放的api.dart,wei调用的api配置文件
- models文件存放的实体层
- screen文件夹存放的页面view层
- tabs存放的底部切换文件夹层
- widgets存放的组件,包含视频播放组件player.dart以及左右等描述组件
功能介绍
主要的依赖组件,请使用国内镜像下载,切记切记!!!!
flutter: sdk: flutter flutter_svg: ^0.17.4 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.3 cached_network_image: ^2.2.0 json_annotation: ^3.0.1 font_awesome_flutter: ^8.8.1 http: ^0.12.0+4 provider: ^4.0.4 avatar_glow: any getflutter: ^1.0.11 flutter_money_formatter: ^0.8.3 video_player: ^0.10.8+1 dio: ^3.0.9 dio_cookie_manager: ^1.0.0
包含字体文件,主要为抖音自带的字体文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import
'package:flutter/widgets.dart'
;
class DouyinIcons {
DouyinIcons._();
static
const
_kFontFam =
'DouyinIcons'
;
static
const
IconData chat_bubble =
const
IconData(0xe808, fontFamily: _kFontFam);
static
const
IconData create =
const
IconData(0xe809, fontFamily: _kFontFam);
static
const
IconData heart =
const
IconData(0xe80a, fontFamily: _kFontFam);
static
const
IconData home =
const
IconData(0xe80b, fontFamily: _kFontFam);
static
const
IconData messages =
const
IconData(0xe80c, fontFamily: _kFontFam);
static
const
IconData profile =
const
IconData(0xe80d, fontFamily: _kFontFam);
static
const
IconData reply =
const
IconData(0xe80e, fontFamily: _kFontFam);
static
const
IconData search =
const
IconData(0xe80f, fontFamily: _kFontFam);
}
|
此次采用Flutter开发安卓、IOS等 app确实方便,主要为将tiktok的数据使用http下载下来.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
import
'package:http/http.dart'
as http;
class RequestController {
static String host =
"https://www.tiktok.com/"
;
String url = host +
"/share/item/list?secUid=&id=&type=5&count=30&minCursor=0&maxCursor=0&shareUid=&lang=en&_signature=pKb.ogAgEB9ImoSQahoqJKSm.rAAPox"
;
Future<String> getCookie() async {
try {
var
response = await http.get(host +
"/share/item/"
);
return
response.headers[
"set-cookie"
];
} catch (e) {
return
"error"
;
}
}
|
Model层
主要为实体层,解析json后绑定数据以及传递数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
class Tiktok {
int statueCode;
Body body;
Object errMsg;
Tiktok({this.statueCode, this.body, this.errMsg});
Tiktok.fromJson(Map<String, dynamic> json) {
statueCode = json[
'statusCode'
];
body = json[
'body'
] != null ? new Body.fromJson(json[
'body'
]) : null;
errMsg = json[
'errMsg'
];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data[
'statusCode'
] = this.statueCode;
if
(this.body != null) {
data[
'body'
] = this.body.toJson();
}
data[
'errMsg'
] = this.errMsg;
return
data;
}
}
|
视图层
另外屏幕层主要包含三个,homescreen,trendingscreen,以及显示videoscreen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
import
'package:flutter/material.dart'
;
import
'package:flutter_app/Screens/trendingScreen.dart'
;
import
'package:flutter_app/widgets/bottom_toolbar.dart'
;
class Home extends StatefulWidget {
@override
HomeState createState() => HomeState();
}
class HomeState extends State<Home> {
int currentIndex = 0;
PageController pageController;
@override
Widget build(BuildContext context) {
return
Scaffold(
body: PageView(
controller: pageController,
children: <Widget>[
Trending(),
],
onPageChanged: (int index) {
setState(() {
currentIndex = index;
});
},
),
bottomNavigationBar: bottomItems(currentIndex, pageController),
);
}
}
|
Tending层,主要包含读取抖音的api,将api转化成实体对象,绑定数据到videoscreen页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
import
'dart:convert'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/cupertino.dart'
;
import
'package:getflutter/getflutter.dart'
;
import
'package:flutter_app/config/api.dart'
;
import
'package:flutter_app/models/Tiktok.dart'
;
import
'package:http/http.dart'
as http;
import
'package:flutter_app/Screens/videoScreen.dart'
;
class Trending extends StatefulWidget {
_TrendingState createState() => _TrendingState();
}
class _TrendingState extends State<Trending> {
PageController pageController;
BuildContext context;
RequestController api = RequestController();
List<Widget> videos = [];
getTrending() async {
var
cookies = await api.getCookie();
api.setCookie(cookies);
try {
var
response = await http.get(
api.url,
headers: api.headers,
);
Tiktok tiktok = Tiktok.fromJson(jsonDecode(response.body));
tiktok.body.itemListData.forEach(
(item) {
setState(() {
videos.add(VideoItem(data: item));
});
},
);
} catch (ex) {
SimpleDialog(
title: Text(
'Hot videos list is empty'
),
);
print(ex);
}
}
@override
void initState() {
super.initState();
getTrending();
}
@override
Widget build(BuildContext context) {
context = context;
return
PageView(
scrollDirection: Axis.vertical,
controller: pageController,
children: videos.length == 0
? <Widget>[
Container(
color: Colors.black,
child: Center(
child: GFLoader(
type
: GFLoaderType.circle,
loaderColorOne: Colors.blueAccent,
loaderColorTwo: Colors.white,
loaderColorThree: Colors.pink,
),
),
)
]
: videos,
);
}
}
|
VideoScreen主要为绑定数据. 展示抖音的视频
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
import
'package:flutter/material.dart'
;
import
'package:flutter_app/models/Tiktok.dart'
;
import
'package:flutter_app/widgets/video_description.dart'
;
import
'package:flutter_app/widgets/actions_toolbar.dart'
;
import
'package:flutter_app/widgets/player.dart'
;
class VideoItem extends StatelessWidget {
final ItemListData data;
const
VideoItem({@required this.data});
@override
Widget build(BuildContext context) {
return
Scaffold(
body: Stack(
children: <Widget>[
DouyinVideoPlayer(
url: data.itemInfos.video.urls[0],
),
title(),
VideoDescription(
description: data.itemInfos.text,
musicName: data.musicInfos.musicName,
authorName: data.musicInfos.authorName,
userName: data.authorInfos.uniqueId,
),
ActionsToolbar(
comments: data.itemInfos.commentCount.toString(),
userImg: data.authorInfos.covers[0],
favorite: data.itemInfos.diggCount,
coverImg: data.musicInfos.covers[0],
),
],
),
);
}
Widget title() => Align(
alignment: Alignment.topCenter,
child: Padding(
padding: EdgeInsets.symmetric(vertical: 28.0),
child: Text(
"Trending | For You"
,
style: TextStyle(color: Colors.white, fontSize: 19.0),
),
),
);
}
|
此次开发主要时间用在搭建Flutter环境上,切记使用国内镜像,另外调式需要配合代理即可。
其他待完成的包含底部的导航页面,打算花两天时间把剩余的完成.
各位感兴趣的可以到我的github上点一下star. 留言可以教你们开发以及搭建dart环境. 地址:https://github.com/WangCharlie/douyin
感兴趣的各位留言哈.
作者:风清扬 No.1
出处: http://www.cnblogs.com/fengqingyangNo1
如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的 【推荐】按钮。
如果,您希望更容易地发现我的新博客,不妨点击一下右下角的 【关注 风清扬 No.1】。
因为,我的写作热情也离不开您的肯定支持。
感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客.
出处: http://www.cnblogs.com/fengqingyangNo1
如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的 【推荐】按钮。
如果,您希望更容易地发现我的新博客,不妨点击一下右下角的 【关注 风清扬 No.1】。
因为,我的写作热情也离不开您的肯定支持。
感谢您的阅读,如果您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客.