使用到的控件及属性记录
项目创建完成后会有main.dart文件,里面有main方法,调用了MyApp控件,在MyApp控件的build方法里MaterialApp控件的home属性中编写页面内容。
跨平台技术比较:
Flutter、ReactNative、Weex、Ionic
Ionic 实际是H5页面外面套了Android的壳子
ReactNative 实际上是桥接方案,通过JS代码封装的IOS以及Android的控件,不用直接使用原生控件。但底层还是使用原生控件进行的布局
Flutter 比RN实现得更为底层,没有使用桥接,直接通过原生的底层框架实现页面效果,渲染效率更强
StatelessWidget 纯展示控件,不需要私有数据的控件
StatefulWidget 需要访问网络,数据需要动态变化的控件
BottomNavigationBar 底部的TabBar,如使用需要将其父节点用DefaultTabController包裹
DefaultTabController 使用需指定TabBar的个数
Scaffold 封装了页面美化效果的控件,一般都做整个页面的相对父节点使用。里面封装了AppBar,Drawer,TabBar,TabBarView等控件
Scaffold 的bottomNavigationBar属性一般传入TabBar,如果控件有些属性无法设置,则在外面包裹一个Container再看是否可以设置
Scaffold 的appBar属性一般传入AppBar ,通过actions属性添加行为按钮点击
ListView 设置固定的垂直列表,使用ListTile即可
Padding值的设置,全部设置EdgeInsets.all(0);单独设置EdgeInsets.only(0)
UserAccountsDrawerHeader控件 侧边栏经常会使用到
CircleAvatar 圆形图片控件,backgroundImage属性中设置网络图片或是本地图片
如果一个控件不能设置颜色、背景等,则设置一个装饰器decoration属性,传入一个BoxDecoration控件,看是否能设置
GestureDetector 给某个控件设置点击事件,onTap点击事件属性,child点击的控件
Divider 分割线控件
构造传参的固定写法:MovieList({Key key, @required this.tabFlag}) : super(key: key);
在状态管理类要想获取到构造传参数据需要使用widget. 去获取
可变状态控件以及其状态管理类也有固定的写法
如果可变状态控件的管理类,有状态需要保存,则在管理类中with AutomaticKeepAliveClientMixin
ListView控件和ListVIew.builder()的区别:ListView只能展示代码写死的内容,不能动态更改,ListView.builder()可以动态更改
ListView.builder() itemCount属性表示item的个数,itemBuilder属性表示每一个item项
页面路由跳转:Navigator.push(context, MaterialPageRoute(builder: (BuildContext context)
Text控件 文字控件
Image控件 图片控件
Row 横向控件,行的意思
Column 纵向控件,列的意思。crossAxisAlignment横向属性设置,mainAxisAlignment纵向属性设置
Dio网络请求:async await dio.get(); setState(() {} 在setState方法中进行私有数据的赋值,否则不会更新页面
需要使用类以及第三方库,需要手动在dart文件中导包
示例代码
// main.dart
// 导入控件
import 'package:flutter/material.dart';
// 手动引入另一个dart文件
import "./body.dart";
// 入口函数 运行MyApp这个控件
void main() => runApp(MyApp());
// MyApp控件继承自无状态控件
class MyApp extends StatelessWidget {
// 每一个控件都要重写 build函数
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '快播二代',
theme: ThemeData(
primarySwatch: Colors.yellow,
),
// flutter里面每一个控件都是一个类,里面可以省略new
home: new MyTest());
}
}
class MyTest extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Scaffold中有一些封装好的控件可供调用
// 使用TabBar 需要同步的使用TabControler
return DefaultTabController(
length: 4,
child: Scaffold(
// 顶边栏控件
appBar: AppBar(
title: Text("电影列表"),
// 标题居中属性
centerTitle: true,
// 右侧行为按钮
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
// 点击事件
onPressed: () {})
]),
// 侧边栏控件
drawer: Drawer(
child: ListView(
// 取消listview的内padding
padding: EdgeInsets.all(0),
// listview中的每一个item布局
children: <Widget>[
// 第一个item是一个有头像和背景图片的布局,使用UserAccountsDrawerHeader控件
UserAccountsDrawerHeader(
accountName:
Text("ZHR", style: TextStyle(color: Colors.white)),
accountEmail: Text("developerzhr@gmail.com",
style: TextStyle(color: Colors.white)),
currentAccountPicture: CircleAvatar(
backgroundImage: NetworkImage(
"https://img.zcool.cn/community/01a7f35d9587a1a8012060be0d6d90.jpg@1280w_1l_2o_100sh.jpg")),
// 美化当前控件的
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
"https://img.zcool.cn/community/01c4155d9587a2a801211d53c7142f.jpg@1280w_1l_2o_100sh.jpg"),
// cover 是fitcenter
// fill 是fitxy
fit: BoxFit.cover))),
GestureDetector(
onTap: () {
print("点击用户反馈");
},
child: ListTile(
title: Text("用户反馈"), trailing: Icon(Icons.feedback))),
GestureDetector(
onTap: () {
print("点击系统设置");
},
child: ListTile(
title: Text("系统设置"), trailing: Icon(Icons.settings))),
GestureDetector(
onTap: () {
print("点击我要发布");
},
child: ListTile(
title: Text("我要发布"), trailing: Icon(Icons.send))),
Divider(),
GestureDetector(
onTap: () {
print("点击立即退出");
},
child: ListTile(
title: Text("立即退出"), trailing: Icon(Icons.exit_to_app)))
])),
// 底部的TabBar 如果要修改TabBar的属性就需要在TabBar外包一个Container
bottomNavigationBar: Container(
color: Colors.yellow,
// 一般的TabBar高度都为50
height: 50,
child: TabBar(
// 设置TabBar的高度及字体大小
labelStyle: TextStyle(height: 0, fontSize: 10),
tabs: <Widget>[
Tab(text: "正在热映", icon: Icon(Icons.movie_filter)),
Tab(text: "即将上映", icon: Icon(Icons.movie_creation)),
Tab(text: "Top250", icon: Icon(Icons.aspect_ratio)),
Tab(text: "本地电影", icon: Icon(Icons.local_movies))
])),
// 内容控件,一般都设置为StatefulWidget
body: TabBarView(
children: <Widget>[
// 构造方法传参
MovieList(tabFlag: "in_theaters"),
MovieList(tabFlag: "top250"),
MovieList(tabFlag: "in_theaters"),
MovieList(tabFlag: "top250")
],
),
));
}
}
------------------------------------------------------------------
// body.dart
import 'package:flutter/material.dart';
import './detail.dart';
// 导入网络请求框架
import "package:dio/dio.dart";
// ctrl + alt + L 格式化代码
Dio dio = new Dio();
// 创建一个电影列表控件 是可变状态控件 需要创建一个状态管理类
class MovieList extends StatefulWidget {
final String tabFlag;
// 构造函数的固定写法
// MovieList({Key key}): super(key : key);
// 带传参的构造函数写法 @required 表示此参数为必传项
MovieList({Key key, @required this.tabFlag}) : super(key: key);
// 固定格式和写法
@override
_MovieListState createState() {
return _MovieListState();
}
}
// 状态管理的的固定格式和写法 如果想要有保存当前状态能力用with keepalive
class _MovieListState extends State<MovieList> with AutomaticKeepAliveClientMixin{
// 默认显示第一页
int page = 1;
// 每一页显示十条数据
int pageSize = 10;
// 电影列表数据
var movieList = [];
@override
bool get wantKeepAlive => true;
// 控件被创建的时候会执行initState此生命周期方法
@override
void initState() {
super.initState();
// 请求网络获取电影列表数据
getMovieList();
}
@override
Widget build(BuildContext context) {
// 如果在状态管理类要使用控件的传参 需要使用widget.来调用私有数据使用
// return Text("tabFlag:${widget.tabFlag},---${movieList.length}");
// ListView只能展示代码写死的内容,不能动态更改,ListView.builder可以动态更改
return ListView.builder(
itemCount: movieList.length,
itemBuilder: (BuildContext context, int index) {
var movieItem = movieList[index];
// This function has a return type of 'Widget', but doesn't end with a return statement
// 如果有些属性添加不了,就想着在外面套一层Container
return GestureDetector(
onTap: () {
// print("测试点击事件");
// 给点击事件添加路由跳转地址
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) {
// return Text("测试点击事件");
return MovieDetail(
id: movieItem["id"], title: movieItem["title"]);
}));
},
child: Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: Colors.black12))),
// 返回一个行展示数据
child: Row(
children: <Widget>[
Image(
image: NetworkImage(movieItem["images"]["small"]),
width: 130,
height: 180,
fit: BoxFit.cover),
Container(
padding: EdgeInsets.only(left: 10),
height: 180,
child: Column(
// 横向属性设置 居左对齐
crossAxisAlignment: CrossAxisAlignment.start,
// 纵向属性设置 居中对齐
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Text("电影名称:${movieItem["title"]}"),
Text("上映年份:${movieItem["year"]}年"),
// 集合中参数要添加的内容用join插入
Text("电影类型:${movieItem["genres"].join("*")}"),
Text("豆瓣评分:${movieItem["rating"]["average"]}"),
Row(children: <Widget>[
Text("主要演员:"),
CircleAvatar(
backgroundImage: NetworkImage(
movieItem["casts"][0]["avatars"]
["small"]))
])
],
))
],
)));
},
);
}
// 获取电影列表数据的方法
getMovieList() async {
// 模版字符串 两种表现方式${.}和$
// 偏移量计算公式 (page - 1) * pageSize
int offSet = (page - 1) * pageSize;
// 请求网络需要异步 sync和await搭配使用
var response = await dio.get(
// http://www.liulongbin.top:3005/api/v2/movie/top250?start=0&count=1
"http://www.liulongbin.top:3005/api/v2/movie/${widget.tabFlag}?start=$offSet&count=$pageSize");
// 服务器返回的真正数据
var data = response.data;
print("data:${data.toString()}");
// 网络上请求的数据都需要在setState方法中进行私有数据的赋值,否则页面不会更新
setState(() {
// 数据实体中的键需要通过[]进行匹配调用
// 把subjects 数据赋值给了movieList 其他地方使用直接取出来就可以了
movieList = data["subjects"];
pageSize = data["total"];
});
}
}
------------------------------------------------------------------
// detail.dart
import 'package:flutter/material.dart';
class MovieDetail extends StatefulWidget {
final String id;
final String title;
// 构造传参,传入点击的item对应的电影id和名称,根据这两个参数去请求不同的详情内容
MovieDetail({Key key, @required this.id, @required this.title})
: super(key: key);
@override
_MovieDetailState createState() {
return _MovieDetailState();
}
}
class _MovieDetailState extends State<MovieDetail> {
@override
Widget build(BuildContext context) {
// return Text("测试点击事件${widget.id},,${widget.title}");
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Center(
child: Text(
"电影id:${widget.id}",
style: TextStyle(fontSize: 22),
)));
}
}
上面Demo 点击此处下载