先说明一下
这里不讲怎么安装flutter环境等前置操作(就提一些我安装的时候踩的坑),我直接讲自己开发的实际经历(其实也就开发了两到三个星期)。flutter是牛逼,但是生态环境是真的少,目前还是RN比较多人用。
安装遇到的坑:
- flutter sdk最好不要安装1.17.0,因为我自己安装的时候执行命令行flutter run一直报错,各种各样的错,我换成低版本一点的就不会,截止目前(文章发表日期)时间。以后不会报错再说。
- sdk下载太慢的话可以换迅雷下载,挺快的。
- 记得开启虚拟设置(一般第一次安装都需要进入BIOS界面去修改),不会的朋友自行百度,谢谢。
- 在环境变量里面记得配置国内镜像(PUB_HOSTED_URL:pub.flutter-io.cn**和**FLUTTER_STORAGE_BASE_URL:https://storage.fl…)
- 顺便在环境变量里面添加命令行
开始学习
当你可以开始运行flutter并且可以看到类似下面的图片的时候,恭喜你,这里就是从零开始。
基础学习资料
这里推荐先去看看官方文档的东西,可以先大概过一下,因为刚开始的时候是记不住那么多东西,只要有个印象就可以。不用死记硬背,需要的时候再去查就可以,因为我自己现在也是这样,哈哈哈哈。
学习视频
我又来给jspang大神推广了......
推荐必看知识
Flutter实战 一些基础知识(而且有一说一,里面讲到的组件是真的少,不过讲到的都说的不错,毕竟不是主讲组件的)
Flutter中文网 这个一定要去看一下里面的 Flutter for Web开发者这一章,不需要很懂,但需要大概了解一下(我自己是web开发者,之前也没有接触过其他app框架)
后面需要用到的时候再看
flutter组件网这里推荐一个讲组件的网站,虽然不是全部,但也挺多了。
基础知识
dart语法
我是觉得有点像java(我的java只停留在大学课本级别),这个你如果看完上面flutter实战应该多少会有一些了解,那就足够了其实,我自己也没有特地去学习dart,只是在需要的时候再去查,因为很多东西和js还是很像的,不过它有一些很好用的操作符,例如?./??等等......
Widget简介
官网介绍:在Flutter中几乎所有的对象都是一个Widget。与原生开发中“控件”不同的是,Flutter中的Widget的概念更广泛,它不仅可以表示UI元素,也可以表示一些功能性的组件如:用于手势检测的 GestureDetector widget、用于APP主题数据传递的Theme等等,而原生开发中的控件通常只是指UI元素。在后面的内容中,我们在描述UI元素时可能会用到“控件”、“组件”这样的概念,读者心里需要知道他们就是widget,只是在不同场景的不同表述而已。由于Flutter主要就是用于构建用户界面的,所以,在大多数时候,读者可以认为widget就是一个控件,不必纠结于概念。
这里主要是要了解StatelessWidget和StatefulWidget的区别,这两个官网讲得都挺详细的。(看不太懂的话,有个大概了解就可以,因为我也不是很懂,后面用着用着就知道了)
StatelessWidget(个人理解)
类似一个静态页面/组件,不需要进行数据交互的时候或者说仅依赖于对象本身的配置信息,仅仅是用于展示给定的信息,可以继承这个。
StatefulWidget(个人理解)
需要进行数据交互等操作的时候继承这个(大多情况下是这个),这个多了初始化(initState)、销毁(dispose)等可以重载的回调函数。
简单总结一下,当我们的Widget是StatelessWidget,那么当他的内容被创建出来之后,就不能再改变了。相反StatefulWidget就可以。
基础组件库?material牛逼
这个就是官方说得基础组件库,也就是我们开发当中经常用到的。说是组件库,我个人觉得有点像一个页面的主骨干,要往上面添加什么东西就看自己需要。为什么这样说呢?因为它为我们提供了以个页面的基本骨架(顶部标题、主体、底部导航等等)。例如我们初始化一个项目后的代码(lib/main.dart为例),这里可以大概看一下,不懂的往下看自然就明白了
import 'package:flutter/material.dart';//引入material组件
void main() => runApp(MyApp());//主入口
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,//主题色
),
home: MyHomePage(title: 'Flutter Demo Home Page'),//这个就是主页显示的组件,MyHomePage在下面定义(title是传入的参数)
);
}
}
//下面的代码也可以直接和上面的代码进行合并,不过大家一般会拆开来写,这样好看,容易维护,也体现了组件化思想
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);//这个是用来获取传进来的参数的,接收了上面的title
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
//初始化数据,dart语法在第一次赋值的时候就就需要定义类型,之后不可以再转换,不像js可以随便换
//可以用var来定义一个变量,但当给变量第一次赋值时候就会自动定义类型
int _counter = 0;
//自己写的一个函数
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
//顶部标题栏,title就是接收的参数
appBar: AppBar(
title: Text(widget.title),
),
//主体
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
//这个是悬浮按钮
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,//绑定事件
tooltip: 'Increment',
child: Icon(Icons.add),
),
//底部导航
bottomNavigationBar: BottomAppBar(
color: Colors.white,
child: Row(
children: [
IconButton(icon: Icon(Icons.home)),
IconButton(icon: Icon(Icons.business)),
],
mainAxisAlignment: MainAxisAlignment.spaceAround, //均分底部导航栏横向空间
),
),
);
}
}
组件?flutter万物皆组件?
下面不会提到所有,只提一些我们平常开发(其实是我自己开发时)比较常用的。
基础组件(先说几个比较简单常用的)
这里指的是可以显示出来的组件(我自己这样认为),例如文字(不像web那样直接放的),图片等。而且这里不是类型所有都讲,想看具体请自行看官网或者百度
文本
最基本的使用方法
Text('我是最简单的文本'),
Text(
'我是居中的文本',
textAlign: TextAlign.center, //这个居中是相对于它自身所在位置(不是相对于屏幕),果它所在的位置的宽度变宽,它的位置也会改变,我这里使用的是ListView(下面会说到)
),
Text(
'我是限制了行数的文本' * 9,
maxLines: 1,//最大行数
overflow: TextOverflow.ellipsis,//超出时显示的方式
),
文本添加样式
Text(
'我是有样式的文本',
style: TextStyle(
backgroundColor: Colors.blue, //背景色 Colors是直接用官方给的颜色
color: Color.fromRGBO(100, 100, 100,
1), //字体颜色 Color.fromRGBO(red,green,bule,opacity)可以自定义颜色,还有一个是Color.fromARGB
fontSize: 15,
height: 15, //该属性用于指定行高,但它并不是一个绝对值,而是一个因子,具体的行高等于fontSize*height
decoration: TextDecoration.lineThrough,//下划线,上划线等
decorationStyle: TextDecorationStyle.dashed,//上面那个属性的样式,实线,虚线什么的
),
)
一个Text使用多种样式
Text.rich(
TextSpan(
children: [
TextSpan(text: "第一种样式"),
TextSpan(
text: "第二种样式",
style: TextStyle(color: Colors.blue),
),
],
),
)
按钮
RaisedButton(
child: Text("我是简单的按钮"),
onPressed: () {},
),
IconButton(
icon: Icon(Icons.thumb_up),
onPressed: () {},
),
RaisedButton.icon(
//这个还有其他差不多样式 就是换个名字而已RaisedButton/OutlineButton/FlatButton
icon: Icon(Icons.account_balance_wallet),
label: Text("icon+文本"),
onPressed: () {},
),
FlatButton(
color: Colors.blue,
highlightColor: Colors.green[700],
colorBrightness: Brightness.dark, //按钮主题 深色或浅色
splashColor: Colors.grey,
child: Text("没有icon自定义"),
shape: RoundedRectangleBorder(//形状
borderRadius: BorderRadius.circular(20.0),//圆角
),
onPressed: () {},
),
FlatButton.icon(
color: Colors.blue,
icon: Icon(Icons.thumb_up),
highlightColor: Colors.blue[700],
colorBrightness: Brightness.dark,
splashColor: Colors.grey,
label: Text("有icon自定义"),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
onPressed: () {},
)
图片/icon
Image(
image: AssetImage('images/demo/img1.jpg'),//为什么这样写下面会讲到
width: 100.0,
),
Image.asset(
//等同于上面的写法
"images/demo/img2.jpg",
width: 100.0,
),
Image.network(
//引用网络的
'https://cdn.jsdelivr.net/gh/flutterchina/website@1.0/images/flutter-mark-square-100.png',
),
Icon(Icons.home),
Icon(
Icons.home,
color: Colors.blueAccent,
size: 100,
),
这里图片的用法和我们web的引用相对路径不用,需要先在配置文件里面先定义好
官方说法
输入框
TextField(
autofocus: false,//是否有自动聚焦
keyboardType: TextInputType.number, //输入框形式 文本、数字等
onChanged: (value) {
print(value);
//赋值操作
},
),
样式什么的太多了,这里就不提了,这里的数字输入框在ios端是没有完成按钮的,解决方法在我另外一篇flutter踩坑日记里面就有解决方法
容器?我的div呢?
Container(这不就是我想要的div吗)
Container,顾名思义,就是一个容器,用来存放你的其他组件(文本,图片什么的),它本身并没有特别的含义(类似我们的div,可以这样说)。当然,它肯定不止用来放组件这么简单,它定义其他一些属性,例如边框、背景这些样式。
最最最简单的例子
Container(
child: Text('我的父级就是一个Container容器'),
),
好了,container就说这些了,怎么可能,这个可是我最喜欢的,开发中我最常用的。
Container(
width: 100,
height: 100,
// margin: EdgeInsets.all(10),//外边距,这种是全部
// margin: EdgeInsets.only(left: 200, top: 20), //这种是单独的
margin: EdgeInsets.fromLTRB(10, 20, 30, 40), //这种是四个
padding: EdgeInsets.all(10), //内边距,同上
// color: Colors.blueAccent, //背景色,记得这个会和decoration里面的color冲突,如果有decoration就需要在decoration里面设置,不能在外面
// foregroundDecoration: BoxDecoration(//也是装饰,但是会绘制在 child 之上,也就是会覆盖 child(很少用到)
// color: Colors.blue,
// ),
decoration: BoxDecoration(
color: Colors.greenAccent, //背景色
border: Border.all(color: Colors.blueAccent, width: 5), //全部
// border: Border(
// top: BorderSide(color: Colors.pinkAccent, width: 5),//单个
// right: BorderSide(color: Colors.blueAccent, width: 5),
// ),
borderRadius: BorderRadius.all(
//全部
Radius.circular(10),
),
// borderRadius: BorderRadius.only(//单个
// topLeft: Radius.circular(10),
// bottomRight: Radius.circular(20),
// ),
// gradient: LinearGradient(
// //背景渐变
// colors: [Colors.red, Colors.green, Colors.blue],
// ),
boxShadow: [
BoxShadow(
color: Colors.pinkAccent,
blurRadius: 25.0, //延伸距离,会有模糊效果
offset: Offset(10.0, -5.0), //X轴 Y轴偏移量
spreadRadius: 9.0, //延伸距离,不会有模糊效果
)
],
),
child: Text('文本'), //子元素
),
因为这里的显示的不同东西太多,就干脆只放一张最终的了(不然写一下放一张怕是要十几张)
布局组件?这又是什么?
布局类组件都会包含一个或多个子组件,不同的布局类组件对子组件排版(layout)方式不同(后面的这句画重点)
Row/Column/Expanded(flex???)
还是顾名思义,典型的行列布局
这里没有提到溢出的情况(画重点),因为溢出问题需要用到其他组件(滚动组件),如果你出现了溢出情况请耐心地往下看。
Row
Row(
mainAxisSize:MainAxisSize.max,//Row在主轴(这里的主轴是相对而言的,Row的主轴是水平的 Column的主轴是垂直的)方向占用的空间,
mainAxisAlignment: MainAxisAlignment.start,//子组件在主轴的(同上)上的对齐方式,当mainAxisSize为min是没有意义
verticalDirection:VerticalDirection.down,//表示Row纵轴(垂直)的对齐方向,默认是VerticalDirection.down,表示从上到下
crossAxisAlignment:CrossAxisAlignment.start,//子组件在副轴(副轴是我自己喜欢的叫法)(row的是指垂直)的(同上)上的对齐方式,可以看到当我
textDirection: TextDirection.ltr,//子组件布局顺序 ltr表示从左开始布局(默认),rtl表示从右开始布局
children: <Widget>[
Text('行布局'),
RaisedButton(
child: Text("我是简单的按钮"),
onPressed: () {},
),
Image(
image: AssetImage('images/demo/img1.jpg'),
width: 100.0,
),
Image(
image: AssetImage('images/demo/img1.jpg'),
width: 100.0,
),
],
),
Column
这个其实和行布局差不多的,只是换成垂直方向而已,记得Column的主轴是垂直的,副轴是水平的
Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,//我这里相对上面的row布局换了参数
crossAxisAlignment: CrossAxisAlignment.center,//我这里相对上面的row布局换了参数
textDirection: TextDirection.ltr,
children: <Widget>[
Text('列布局'),
RaisedButton(
child: Text("我是简单的按钮"),
onPressed: () {},
),
Image(
image: AssetImage('images/demo/img2.jpg'),
width: 100.0,
),
Image(
image: AssetImage('images/demo/img2.jpg'),
width: 100.0,
),
],
),
Expanded
这个是觉得Flutter实战讲的很到位,我就直接搬过来了。
如果Row里面嵌套Row,或者Column里面再嵌套Column,那么只有最外面的Row或Column会占用尽可能大的空间,里面Row或Column所占用的空间为实际大小
Container(
color: Colors.green,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max, //有效,外层Colum高度为整个屏幕
children: <Widget>[
Container(
color: Colors.red,
child: Column(
mainAxisSize: MainAxisSize.max,//无效,内层Colum高度为实际高度
children: <Widget>[
Text("hello world "),
Text("I am Jack "),
],
),
)
],
),
),
);
如果要让里面的Column占满外部Column,可以使用Expanded 组件
Expanded(
child: Container(
color: Colors.red,
child: Column(
mainAxisAlignment: MainAxisAlignment.center, //垂直方向居中对齐
children: <Widget>[
Text("hello world "),
Text("I am Jack "),
],
),
),
)
Stack/Positioned
这个我觉得有点像我们web开发中的父相子绝(父元素相对定位,子元素绝对定位),flutter好像没有相对屏幕进行定位的(例如我们的position:fixed)。记得Positioned这个是需要和Stack在一起嵌套使用的。
Stack(
alignment: AlignmentDirectional
.bottomStart, //此参数决定如何去对齐没有定位(没有使用Positioned)或部分定位的子组件
overflow: Overflow
.visible, //此属性决定如何显示超出Stack显示空间的子组件 Overflow.clip(裁剪/隐藏) Overflow.visible(可见)
textDirection: TextDirection.ltr, //这个和row布局一样
fit: StackFit.loose,//此参数用于确定没有定位的子组件如何去适应Stack的大小 loose表示使用子组件的大小,expand表示扩伸到Stack的大小。
children: <Widget>[
Positioned(
top: 100, //相对Stack的定位
left: 10,
width: 100,
height: 100,
child: Container(
color: Colors.blueAccent,
),
),
Positioned(
//假如位置相同,写在后面的组件会覆盖前面的组件,上面设置了蓝色,这里设置了粉色,但是只能看见粉色
top: 100,
left: 10,
width: 100,
height: 100,
child: Container(
color: Colors.pinkAccent,
),
),
Positioned(
//覆盖了上面的一半
top: 150,
left: 10,
width: 100,
height: 100,
child: Container(
color: Colors.orangeAccent,
),
),
Container(
color: Colors.greenAccent,
child: Text('我没有设置定位'),
),
],
),
可见和隐藏的区别
布局组件就讲到这里,还有其他布局组件如果需要可以直接看官网或者百度。
滚动组件?不会自己动起来吗?溢出?什么鬼?不会自动隐藏的吗?
本来想和溢出分开讲的,但感觉合在一起讲好像更好就合起来了。
这里先说一下溢出是什么情况,当我们设置的子组件超出屏幕范围(例如我在row组件设置了很多张图片,如下图),这种情况我们称为溢出。
那是因为它不会自己动起来呀(如果你觉得这里黄色是因为你带了黄色的眼睛(dog))。。。这时候我们就需要让它动起来。
ListView
最简单的写法,因为我们在开发中用到这个组件的是一般是从接口拿到数据,并不知道有第多少个
ListView(
scrollDirection: Axis.vertical, //滚动方向
padding: EdgeInsets.all(20.0), //内边距
// itemExtent:10,
// shrinkWrap: true,//该属性表示是否根据子组件的总长度来设置ListView的长度,默认值为false 。默认情况下,ListView的会在滚动方向尽可能多的占用空间。当ListView在一个无边界(滚动方向
children: <Widget>[
Container(
height: 150,
color: Colors.pinkAccent,
),
Container(
height: 150,
color: Colors.greenAccent,
),
Container(
height: 150,
color: Colors.blueAccent,
),
Container(
height: 150,
color: Colors.black54,
),
Container(
height: 150,
color: Colors.orangeAccent,
),
Container(
height: 150,
color: Colors.cyanAccent,
),
],
),
更高级的写法,动态生成
import 'package:flutter/material.dart';
class ListViewDemo extends StatefulWidget {
@override
_ListViewDemoState createState() => _ListViewDemoState();
}
class _ListViewDemoState extends State<ListViewDemo>
with SingleTickerProviderStateMixin {
//定义一个数组
List list = [
{'name': '1'},
{'name': '2'},
{'name': '3'},
{'name': '4'},
{'name': '5'},
{'name': '6'},
{'name': '7'},
];
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('滚动组件ListView'),
centerTitle: true,
),
body: ListView.builder(
itemCount: list.length, //数组长度
itemBuilder: (context, index) {//上下文和索引
return Container(//返回一个容器组件
height: 150,
color: Color.fromRGBO(200, 200, index * 30, 1),//这里是我为了更好分辨
child: Text(list[index]['name']),
);
},
),
);
}
}
GridView
这个和上面的大同小异,不做过多讲述。
import 'package:flutter/material.dart';
class GridViewDemo extends StatefulWidget {
@override
_GridViewDemoState createState() => _GridViewDemoState();
}
class _GridViewDemoState extends State<GridViewDemo>
with SingleTickerProviderStateMixin {
List list = [
Icons.ac_unit,
Icons.airport_shuttle,
Icons.all_inclusive,
Icons.tag_faces,
Icons.beach_access,
Icons.cake,
Icons.free_breakfast,
];
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('网格滚动GridView'),
centerTitle: true,
),
body: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 1.0,
),
itemCount: list.length,
itemBuilder: (context, index) {
// return Text(list[index].toString()+'222');
return Icon(list[index]);
},
),
// GridView(
// gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
// crossAxisCount: 3, //横轴三个子widget
// childAspectRatio: 1.0 //子元素在横轴长度和主轴长度的比例。由于crossAxisCount指定后,子元素横轴长度就确定了,然后通过此参数值就可以确定子元素在主轴的长度
// ),
// children: <Widget>[
// Icon(Icons.ac_unit),
// Icon(Icons.airport_shuttle),
// Icon(Icons.all_inclusive),
// Icon(Icons.beach_access),
// Icon(Icons.cake),
// Icon(Icons.free_breakfast)
// ],
// ),
// GridView.count(//和上面相同 写法不一样而已
// crossAxisCount: 3,
// childAspectRatio: 1.0,
// children: <Widget>[
// Icon(Icons.ac_unit),
// Icon(Icons.airport_shuttle),
// Icon(Icons.all_inclusive),
// Icon(Icons.beach_access),
// Icon(Icons.cake),
// Icon(Icons.free_breakfast),
// ],
// ),
);
}
}
进阶知识(这个会慢慢添加,下个应该是http请求封装)
路由?怎么进行路由配置管理?
这里不说Navigator.pushNamed(context, '/路径')这些,这里主要讲的是将路由进行统一管理配置。
第一种 直接在入口文件配置
import 'package:flutter/material.dart';
/* 引入页面 */
import './page/homePage.dart';
import './page/404.dart';
/* demo */
import './demo/index.dart';
import './demo/text.dart';
import './demo/button.dart';
import './demo/imgAndIcon.dart';
import './demo/input.dart';
import './demo/container.dart';
import './demo/row.dart';
import './demo/column.dart';
import './demo/stackPositioned.dart';
import './demo/listView.dart';
import './demo/gridView.dart';
import './demo/customScrollView.dart';
/* 定义路由路径 */
final routes = {
'/': (context) => MyHomePage(),
'/notFind': (context) => NotFind(),
/* demo */
'/demoEntry': (context) => DemoEntry(),
'/textDemo': (context, {arguments}) => TextDemo(),
'/buttonDemo': (context, {arguments}) => ButtonDemo(),
'/imgAndIcon': (context, {arguments}) => ImgAndIcon(),
'/inputDemo': (context, {arguments}) => InputDemo(),
'/containerDemo': (context, {arguments}) => ContainerDemo(),
'/rowDemo': (context, {arguments}) => RowDemo(),
'/columnDemo': (context, {arguments}) => ColumnDemo(),
'/stackPositionedDemo': (context, {arguments}) => StackPositionedDemo(),
'/listViewDemo': (context, {arguments}) => ListViewDemo(),
'/gridViewDemo': (context, {arguments}) => GridViewDemo(),
'/customScrollViewDemo': (context, {arguments}) => CustomScrollViewDemo(),
};
/* 路由拦截(这里可以对跳转的路由参数等进行处理) */
Function onGenerateRoute = (RouteSettings settings) {
final String routeName = settings.name;
final Function buildContext = routes[routeName];
Route route;
print(buildContext);
if (buildContext != null) {
//如果路由存在
if (settings.arguments != null) {
//如果有路由传参的话
route = MaterialPageRoute(
builder: (context) =>
buildContext(context, arguments: settings.arguments));
} else {
route = MaterialPageRoute(builder: (context) => buildContext(context));
}
} else {
//路由不存在跳到指定页面
route =
MaterialPageRoute(builder: (context) => routes['/notFind'](context));
}
return route;
};
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
routes:
routes, //这个是下面那个两个有一个就可以,不过下面的onGenerateRoute可以进行拦截,两个同时使用的话只有routes会生效
onGenerateRoute: onGenerateRoute,
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: '/', //入口路由
);
}
}
第二种 另起一个路由配置文件(推荐)
最好是将路由放在另外的文件方便进行管理,我们只需要在主入口文件引入路由文件就可以。 router.dart文件
import 'package:flutter/material.dart';
import '../page/homePage.dart';
import '../page/404.dart';
/* demo */
import '../demo/index.dart';
import '../demo/text.dart';
import '../demo/button.dart';
import '../demo/imgAndIcon.dart';
import '../demo/input.dart';
import '../demo/container.dart';
import '../demo/row.dart';
import '../demo/column.dart';
import '../demo/stackPositioned.dart';
import '../demo/listView.dart';
import '../demo/gridView.dart';
import '../demo/customScrollView.dart';
final routes = {
'/': (context) => MyHomePage(),
'/notFind': (context) => NotFind(),
/* demo */
'/demoEntry': (context) => DemoEntry(),
'/textDemo': (context, {arguments}) => TextDemo(),
'/buttonDemo': (context, {arguments}) => ButtonDemo(),
'/imgAndIcon': (context, {arguments}) => ImgAndIcon(),
'/inputDemo': (context, {arguments}) => InputDemo(),
'/containerDemo': (context, {arguments}) => ContainerDemo(),
'/rowDemo': (context, {arguments}) => RowDemo(),
'/columnDemo': (context, {arguments}) => ColumnDemo(),
'/stackPositionedDemo': (context, {arguments}) => StackPositionedDemo(),
'/listViewDemo': (context, {arguments}) => ListViewDemo(),
'/gridViewDemo': (context, {arguments}) => GridViewDemo(),
'/customScrollViewDemo': (context, {arguments}) => CustomScrollViewDemo(),
};
/* 路由拦截 */
Function onGenerateRoute = (RouteSettings settings) {
final String routeName = settings.name;
final Function buildContext = routes[routeName];
Route route;
// print(buildContext);
if (buildContext != null) {
if (settings.arguments != null) {
route = MaterialPageRoute(
builder: (context) =>
buildContext(context, arguments: settings.arguments));
} else {
route = MaterialPageRoute(builder: (context) => buildContext(context));
}
} else {
route =
MaterialPageRoute(builder: (context) => routes['/notFind'](context));
}
return route;
};
主入口文件
import 'package:flutter/material.dart';
import './router/router.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
// routes: routes,
onGenerateRoute: onGenerateRoute,
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: '/',
);
}
}
第二种是不是看起来好看的多了
http请求封装(暂时还没有,还在弄)
吐槽
包括环境安装(还是在自己的玩游戏电脑上安装了这些),写demo等,前前后后也搞了快一天的时间(可能是我太垃圾了),所以觉得文章有点帮助的,求求给个赞!!!
觉得文章太废的,求求给点意见,谢谢大家
这里附上自己写的另外一篇文章Flutter踩坑日记和github地址