Flutter基礎從-1到0.1(web小白的自述)

歡樂症患者發表於2020-05-24

先說明一下

這裡不講怎麼安裝flutter環境等前置操作(就提一些我安裝的時候踩的坑),我直接講自己開發的實際經歷(其實也就開發了兩到三個星期)。flutter是牛逼,但是生態環境是真的少,目前還是RN比較多人用。

安裝遇到的坑:

  • flutter sdk最好不要安裝1.17.0,因為我自己安裝的時候執行命令列flutter run一直報錯,各種各樣的錯,我換成低版本一點的就不會,截止目前(文章發表日期)時間。以後不會報錯再說。
  • sdk下載太慢的話可以換迅雷下載,挺快的。
  • 記得開啟虛擬設定(一般第一次安裝都需要進入BIOS介面去修改),不會的朋友自行百度,謝謝。
  • 在環境變數裡面記得配置國內映象(PUB_HOSTED_URL:pub.flutter-io.cnFLUTTER_STORAGE_BASE_URL:storage.flutter-io.cn)
    Flutter基礎從-1到0.1(web小白的自述)
  • 順便在環境變數裡面新增命令列
    Flutter基礎從-1到0.1(web小白的自述)

開始學習

當你可以開始執行flutter並且可以看到類似下面的圖片的時候,恭喜你,這裡就是從零開始。

Flutter基礎從-1到0.1(web小白的自述)

基礎學習資料

這裡推薦先去看看官方文件的東西,可以先大概過一下,因為剛開始的時候是記不住那麼多東西,只要有個印象就可以。不用死記硬背,需要的時候再去查就可以,因為我自己現在也是這樣,哈哈哈哈。

學習視訊

我又來給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,//超出時顯示的方式
),
複製程式碼

Flutter基礎從-1到0.1(web小白的自述)
文字新增樣式

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,//上面那個屬性的樣式,實線,虛線什麼的
  ),
)
複製程式碼

Flutter基礎從-1到0.1(web小白的自述)
一個Text使用多種樣式

Text.rich(
  TextSpan(
    children: [
      TextSpan(text: "第一種樣式"),
      TextSpan(
        text: "第二種樣式",
        style: TextStyle(color: Colors.blue),
      ),
    ],
  ),
)
複製程式碼

Flutter基礎從-1到0.1(web小白的自述)

按鈕

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: () {},
)
複製程式碼

Flutter基礎從-1到0.1(web小白的自述)

圖片/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,
),
複製程式碼

Flutter基礎從-1到0.1(web小白的自述)

這裡圖片的用法和我們web的引用相對路徑不用,需要先在配置檔案裡面先定義好

Flutter基礎從-1到0.1(web小白的自述)
官方說法

Flutter基礎從-1到0.1(web小白的自述)

輸入框

TextField(
  autofocus: false,//是否有自動聚焦
  keyboardType: TextInputType.number, //輸入框形式  文字、數字等
  onChanged: (value) {
    print(value);
    //賦值操作
  },
),
複製程式碼

樣式什麼的太多了,這裡就不提了,這裡的數字輸入框在ios端是沒有完成按鈕的,解決方法在我另外一篇flutter踩坑日記裡面就有解決方法

Flutter基礎從-1到0.1(web小白的自述)

容器?我的div呢?

Container(這不就是我想要的div嗎)

Container,顧名思義,就是一個容器,用來存放你的其他元件(文字,圖片什麼的),它本身並沒有特別的含義(類似我們的div,可以這樣說)。當然,它肯定不止用來放元件這麼簡單,它定義其他一些屬性,例如邊框、背景這些樣式。

最最最簡單的例子

Container(
    child: Text('我的父級就是一個Container容器'),
),
複製程式碼

Flutter基礎從-1到0.1(web小白的自述)
好了,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('文字'), //子元素
),
複製程式碼

因為這裡的顯示的不同東西太多,就乾脆只放一張最終的了(不然寫一下放一張怕是要十幾張)

Flutter基礎從-1到0.1(web小白的自述)

佈局元件?這又是什麼?

佈局類元件都會包含一個或多個子元件,不同的佈局類元件對子元件排版(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,
    ),
  ],
),
複製程式碼

Flutter基礎從-1到0.1(web小白的自述)

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,
    ),
  ],
),
複製程式碼

Flutter基礎從-1到0.1(web小白的自述)

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 "),
            ],
          ),
        )
      ],
    ),
  ),
);
複製程式碼

Flutter基礎從-1到0.1(web小白的自述)
如果要讓裡面的Column佔滿外部Column,可以使用Expanded 元件

Expanded( 
  child: Container(
    color: Colors.red,
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center, //垂直方向居中對齊
      children: <Widget>[
        Text("hello world "),
        Text("I am Jack "),
      ],
    ),
  ),
)
複製程式碼

Flutter基礎從-1到0.1(web小白的自述)

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('我沒有設定定位'),
    ),
  ],
),
複製程式碼

可見和隱藏的區別

Flutter基礎從-1到0.1(web小白的自述)
Flutter基礎從-1到0.1(web小白的自述)
佈局元件就講到這裡,還有其他佈局元件如果需要可以直接看官網或者百度。

滾動元件?不會自己動起來嗎?溢位?什麼鬼?不會自動隱藏的嗎?

本來想和溢位分開講的,但感覺合在一起講好像更好就合起來了。

這裡先說一下溢位是什麼情況,當我們設定的子元件超出螢幕範圍(例如我在row元件設定了很多張圖片,如下圖),這種情況我們稱為溢位。

Flutter基礎從-1到0.1(web小白的自述)
那是因為它不會自己動起來呀(如果你覺得這裡黃色是因為你帶了黃色的眼睛(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,
    ),
  ],
),
複製程式碼

Flutter基礎從-1到0.1(web小白的自述)
更高階的寫法,動態生成

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']),
          );
        },
      ),
    );
  }
}
複製程式碼

Flutter基礎從-1到0.1(web小白的自述)

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),
      //   ],
      // ),
    );
  }
}

複製程式碼

Flutter基礎從-1到0.1(web小白的自述)

進階知識(這個會慢慢新增,下個應該是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地址

Flutter基礎從-1到0.1(web小白的自述)

相關文章