基于flutter框架实现的一款简单的应用
这是我在学flutter框架的时候自己写的一个入门级的应用。其中实现了组件的基本使用,基本的网络请求以及json的使用(本地json以及网络接口的调用)等,在下面的文章中,将会介绍这个应用怎样进行界面的设计,怎样实现对应的功能。
应用截图
实现的功能:图片浏览,新闻浏览等常用功能,截图如下:
** 因为我喜欢摄影,所以加上了拍摄技巧,有兴趣的朋友可以自我发挥
一、项目的基本架构
二、具体实现
1、主界面实现
main.dart:
import 'dart:io';
import 'package:built_value/built_value.dart';
import 'package:flutter/material.dart';
import 'package:pictureshow/page/PhotoPage.dart';
import 'package:pictureshow/page/SkillPage.dart';
import 'package:flutter/services.dart';
import 'package:pictureshow/page/AboutPage.dart';
import 'package:pictureshow/page/NewsPage.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
//静态_title字符串
static const String _title = '图片展';
//组件构建器
@override
Widget build(BuildContext context) {
return new MaterialApp( ///MaterialApp风格
debugShowCheckedModeBanner: false,
title: _title,
home: MainStatefulWidget(), ///用MainStatefulWidget()内部类_MainStatefulWidgetState实现home
theme: ThemeData( ///主题风格颜色
primaryColor: Colors.green
),
);
}
}
//类似于一个接口类,StatefulWidget组件
class MainStatefulWidget extends StatefulWidget {
@override
_MainStatefulWidgetState createState() => _MainStatefulWidgetState();
}
/*
类似于一个实现类,继承MainStatefulWidget,而MainStatefulWidget又继承于StatefulWidget
所以MainStatefulWidget继承了StatefulWidget有状态的属性
*/
//持久化
class _MainStatefulWidgetState extends State<MainStatefulWidget> with AutomaticKeepAliveClientMixin<MainStatefulWidget>,SingleTickerProviderStateMixin{
/*保证页面的活跃*/
@override
bool get wantKeepAlive => true; ///see AutomaticKeepAliveClientMixin
int CurrentIndex = 0; ///当前设定底部导航栏的索引
List<StatefulWidget> pageList = [
PhotoPage(),
SkillPage(),
NewsPage(),
];///pageList用来存每个导航栏的组件所对应的页面
@override
//继承StatefulWidget实现组件构建,具体方法
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("优选美图"),
actions: [
PopupMenuButton<String>(
onSelected: (value){ ///当选中时
if(value == 'exit') ///做一个判断,如果返回的value为exit,实现退出程序
SystemNavigator.pop();
else //这样的话下一个按钮就是跳转到“关于”页面
Navigator.push(context,new MaterialPageRoute(builder: (context) => new AboutPage())); ///利用路由跳转
setState(() { ///设置状态,需要传值的话可以设置
});
},
itemBuilder: (context) {
return [
PopupMenuItem<String>(
value: 'about',
child: Text('关于'),
),
PopupMenuItem<String>(
value: 'exit',
child: Text('退出'),
),
];
},
)
],///动作
),
body: this.pageList[this.CurrentIndex], ///主体
bottomNavigationBar: BottomNavigationBar(
//底部导航栏的每一个元素
items:<BottomNavigationBarItem> [
BottomNavigationBarItem(icon: Icon(Icons.photo),title: Text("美图欣赏")),
BottomNavigationBarItem(icon: Icon(Icons.photo_camera),title: Text("拍摄技巧")),
BottomNavigationBarItem(icon:Icon(Icons.new_releases),title:Text("每日新闻")),
],
currentIndex: this.CurrentIndex,///当前索引,指导航栏指向第几个元素
onTap: (index){ ///onTap点击事件,参数是index索引
setState(() { ///设置状态,当被点击之时会产生界面刷新的效果
this.CurrentIndex = index; ///将当前index赋值给CurrentIndex 由此改变body指向的界面
});
},
type: BottomNavigationBarType.fixed, ///显示模式,fixed效果
backgroundColor: Colors.white, ///导航栏背景颜色,默认为白色
),
);
}
@override
void initState() {
// TODO: implement initState
super.initState();
}
}
NewsPage.dart:
class NewsPage extends StatefulWidget {
@override
_NewsPageState createState() => _NewsPageState();
}
var _getnews;///暂存 从接口获取到的数据
class _NewsPageState extends State<NewsPage>{
@override
Widget build(BuildContext context) {
return Center( ///中心布局
child: FutureBuilder(
///future异步处理
future: _getNews(),
builder: (BuildContext context, AsyncSnapshot<Response> snapshot) {
/*表示数据成功返回*/
if (snapshot.hasData) {
Response response = snapshot.data;
var mydata = json.decode(response.toString());
return RefreshIndicator( ///下拉刷新组件
color: Colors.green, ///圆圈进度颜色
displacement: 44.0, ///下拉停止距离
backgroundColor: Colors.grey[200], ///背景颜色
child: ListView.separated(itemCount: mydata == null ? 0 : mydata["result"]["data"].length, ///判断长度
itemBuilder: (BuildContext context, int index) {
return new ListTile(
title: Row(
children: <Widget>[
Expanded(child:
Text(mydata["result"]["data"][index]["title"],
softWrap: true,
overflow: TextOverflow.ellipsis,
maxLines: 2,)
),
],
),
///点击时的事件
onTap: () async {
///路由传参
String result = await Navigator.push(
context, new MaterialPageRoute(builder: (context) => new NewsPageDetailPage(
mydata["result"]["data"][index]["url"])
));
},
);
},
separatorBuilder: (BuildContext context, int index){
return Divider(); ///分割线
},
),
///实现下拉刷新
onRefresh:() async{
if(_getnews != null) {
_getnews = null;
await _getNews();
}
});
}else{
return CircularProgressIndicator();
}
},
),
);
}
}
/**
* 请求接口获取数据
*/
Future<Response> _getNews() async {
///如果缓存的_getnews 为空,则进行数据请求
if(_getnews == null){
await Future.delayed(Duration(seconds: 1), () {
print("延时1秒后请求数据");
});
String url = "http://v.juhe.cn/toutiao/index";
String key = "4c52313fc9247e5b4176aed5ddd56ad7";
String type = "keji";
print("开始请求数据");
Response response = await Dio().get(url, queryParameters: {"type": type, "key": key});
print("请求完成");
_getnews = response;
return response;
}else ///如果缓存的_getnews 不为空,则说明在绘制的时候就已经加载完了,就不需要再请求(太耗流量),则将之前存的_getnews返回给future
{
return _getnews;
}
}
PhotoPage.dart:
class PhotoPage extends StatefulWidget {
@override
_PhotoPageState createState() => _PhotoPageState();
}
class _PhotoPageState extends State<PhotoPage> {
List data;
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center( //Center 中心定位控件
child: FutureBuilder( //FutureBuilder 展示异步任务状态
future: DefaultAssetBundle.of(context).loadString('json/loadJson/photoJson.json'),//加载路径
builder: (context,snapshot){ //builder两个参数 上下文,映射
var mydata = json.decode(snapshot.data.toString()); //json_decode — 对JSON 格式的字符串进行解码
if(mydata==null){
return CircularProgressIndicator();
}else{
return ListView.builder(itemBuilder: (BuildContext context,int index){
return Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children:<Widget> [
Image.network(mydata[index]["photo_src"],width: 400,height: 300,),
// Image.asset("",width: 400,height: 300,), //导入图片
// Text("链接:"+mydata[index]["photo_src"]),
Text("名称:"+mydata[index]["photo_name"],textScaleFactor: 1.2,),
Text("类型:"+mydata[index]["photo_type"],textScaleFactor: 1.2,),
Text("日期:"+mydata[index]["photo_date"],textScaleFactor: 1.2,),
],
),
);
},
itemCount: mydata == null ? 0 : mydata.length, //判断元素长度
);;
}
},
),
),
);
}
}
SkillPage.dart:
class SkillPage extends StatefulWidget {
@override
_SkillPageState createState() => _SkillPageState();
}
class _SkillPageState extends State<SkillPage> {
List data;
var _mydata;
int _index;
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center( //Center 中心定位控件
child: FutureBuilder( //FutureBuilder 展示异步任务状态
future: DefaultAssetBundle.of(context).loadString('json/loadJson/SkillJson.json'),//加载路径
builder: (context,snapshot){ ///builder两个参数 上下文,快照
var mydata = json.decode(snapshot.data.toString()); ///json_decode — 对JSON 格式的字符串进行解码
if(mydata==null){
return CircularProgressIndicator();
}else{
return ListView.separated(
itemCount: mydata == null ? 0 : mydata.length, ///判断长度
itemBuilder: (BuildContext context, int index) {
return new ListTile(
title: Row(
children: <Widget>[
Icon(Icons.circle,
size: 10.0,),
SizedBox(width: 10,),
Expanded(child:
Text(mydata[index]["skill_name"],
softWrap: true,
overflow: TextOverflow.ellipsis,
maxLines: 1,
textScaleFactor: 1.0,
),)
],
),
onTap: () async {
///路由传参
String result = await Navigator.push(
context, new MaterialPageRoute(builder: (context) => new SkillTwoPage(
mydata[index]["skill_name"],
mydata[index]["skill_date"],
mydata[index]["skill_thing"])
));
},
);
},
separatorBuilder: (BuildContext context, int index){
return Divider(); ///分割线
},
);
}
},
),
),
);
}
}
2、Json实体类实现
photo_to_entity.dart:
class PhotoToEntity {
String _photoName;
String _photoType;
String _photoDate;
String _photoSrc;
String get photoName => _photoName;
String get photoType => _photoType;
String get photoDate => _photoDate;
String get photoSrc => _photoSrc;
PhotoToEntity({
String photoName,
String photoType,
String photoDate,
String photoSrc}){
_photoName = photoName;
_photoType = photoType;
_photoDate = photoDate;
_photoSrc = photoSrc;
}
PhotoToEntity.fromJson(dynamic json) {
_photoName = json["photo_name"];
_photoType = json["photo_type"];
_photoDate = json["photo_date"];
_photoSrc = json["photo_src"];
}
Map<String, dynamic> toJson() {
var map = <String, dynamic>{};
map["photo_name"] = _photoName;
map["photo_type"] = _photoType;
map["photo_date"] = _photoDate;
map["photo_src"] = _photoSrc;
return map;
}
}
skill_to_entity.dart:
class Skill_to_entry {
String _skillName;
String _skillDate;
String _skillThing;
String get skillName => _skillName;
String get skillDate => _skillDate;
String get skillThing => _skillThing;
Skill_to_entry({
String skillName,
String skillDate,
String skillThing}){
_skillName = skillName;
_skillDate = skillDate;
_skillThing = skillThing;
}
Skill_to_entry.fromJson(dynamic json) {
_skillName = json["skill_name"];
_skillDate = json["skill_date"];
_skillThing = json["skill_thing"];
}
Map<String, dynamic> toJson() {
var map = <String, dynamic>{};
map["skill_name"] = _skillName;
map["skill_date"] = _skillDate;
map["skill_thing"] = _skillThing;
return map;
}
}
3、界面详细类实现
NewsPageDetailPage.dart:
class NewsPageDetailPage extends StatelessWidget {
///构造
String _news_url;
NewsPageDetailPage(this._news_url);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("每日新闻"),
),
body: Center(
child: WebView(
initialUrl: this._news_url,
)
)
);
}
}
SkillTwoPage.dart:
class SkillTwoPage extends StatelessWidget {
@override
String _skill_name;
String _skill_date;
String _skill_thing;
///构造
SkillTwoPage(this._skill_name,this._skill_date,this._skill_thing);
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this._skill_name),
),
body: Container(
child: ListView(
children: [
Column(
children: <Widget>[
Text(this._skill_thing,
textScaleFactor: 1.2,)
],
),
],
)
),
);
}
}
4、配置文件
pubspec.yaml配置文件主要部分
dependencies:
dio: ^3.0.5
flutter:
sdk: flutter
json_annotation: ^4.0.1
built_value: ^8.0.4
fluttertoast2: ^0.0.8
http: ^0.12.2
async: ^2.5.0
webview_flutter: ^0.3.9+1
cupertino_icons: ^1.0.2
english_words: ^3.1.0
dev_dependencies:
flutter_test:
sdk: flutter
build_runner: ^1.0.0
json_serializable: ^4.1.0
fonts:
- family: myGoodIcon
fonts:
- asset: fonts/goodIcon.ttf
- asset: fonts/menuIcon.ttf
assets:
- assets/image/AppIcon .png
- json/loadJson/photoJson.json
- json/loadJson/SkillJson.json
network_security_config.xml网络请求配置文件主要部分
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>