Flutter基础从-1到0.1(web小白的自述)

1,616 阅读16分钟

先说明一下

这里不讲怎么安装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就是一个控件,不必纠结于概念。

这里主要是要了解StatelessWidgetStatefulWidget的区别,这两个官网讲得都挺详细的。(看不太懂的话,有个大概了解就可以,因为我也不是很懂,后面用着用着就知道了)

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地址