Flutter入門實踐

1.導入包

在pubspec.yaml文件內添加比如url_lanucher: ^4.0.1 

然後就可以dart文件裏導入包import 'package:url_launcher/url_launcher.dart';、

完整代碼:

    import 'package:flutter/material.dart';
    import 'package:url_launcher/url_launcher.dart';


    void main() => runApp(MyApp());

//StatelessWidget表示靜態狀態佈局內部佈局內容不可變,如果裏面內容需要變化就需要繼承StatefulWidget動態佈局組件
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {

//其中的MaterialApp可以在整個應用程序中共享包含顏色和字體樣式的主題
        return MaterialApp(
          title: '使用第三方包示例',

// home: Scaffold表示佈局控件
          home: Scaffold(
            appBar: AppBar(
              title: Text('使用第三方包示例'),
            ),

// body: Center表示佈局全居中
            body: Center(

// child: RaisedButton表示子佈局而且是個按鈕
              child: RaisedButton(

//onPressed內部寫按鈕事件
                onPressed: () {
                    // 指定url併發起請求
                    const url = 'https://www.github.com';
                    // 調用url_launcher包裏的launch方法
                    launch(url);
                },

// child:這個子是在父佈局按鈕下的所以這個text是按鈕的文字內容
                child: Text('打開GitHub'),
              ),
            ),
          ),
        );
      }
    }

最後的效果是一個居中按鈕點擊跳轉網站

2.有狀態及無狀態組件使用

 

        import 'package:flutter/material.dart';


        void main() => runApp(MyApp());


        // MyApp不需要動態修改佈局狀態,所以此組件繼承StatelessWidget即可
        class MyApp extends StatelessWidget {
    // 這個組件是整個應用的主組件
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'Flutter示例',
        theme: ThemeData(
          // 自定義主題
          primarySwatch: Colors.blue,
        ),

//  home: MyHomePage會發現和上面的 home: Scaffold不一樣,因爲這個類裏面有返回Scaffold
        home: MyHomePage(title: '無狀態和有狀態組件示例'),
      );
    }
  }


  // 主頁需要繼承自StatefulWidget
  class MyHomePage extends StatefulWidget {
    MyHomePage({Key key, this.title}) : super(key: key);

    // 標題
    final String title;

    // 必須重寫createState方法
    @override
    _MyHomePageState createState() => _MyHomePageState();
  }
  // 狀態類必須繼承State類,注意後面需要指定爲<MyHomePage>
  class _MyHomePageState extends State<MyHomePage> {
    int _counter = 0; //計數器

//_incrementCounter這個是類裏面的一個私有方法用於外部調用
    void _incrementCounter() {
      // 調用State類裏的setState方法來更改狀態值,使得計數器加1。爲啥放到setState(),因爲調用了setState就會調用佈局生命週期來更新界面

setState(() {
        // 計數器變量,每次點擊讓其加1
        _counter++;
      });
    }

//Widget build這個就是佈局內部返回了return Scaffold對應上面我說的home不同的原因,並且調用setState後這個build會更新
    @override
    Widget build(BuildContext context) {

      return Scaffold(

//appBar: AppBar這塊是頂部標題的佈局還能加左右對齊的佈局比如返回按鈕和下一頁按鈕
        appBar: AppBar(
          title: Text(widget.title),
        ),
        // 居中佈局
        body: Center(

          // 垂直佈局,還可以是水平佈局也就是行佈局
          child: Column(
          // 主軸居中對齊
            mainAxisAlignment: MainAxisAlignment.center,

//children: <Widget>因爲這個佈局是在child裏所以使用children


            children: <Widget>[

//默認水平佈局2個text,第二個text是動態的一個參數使用$來獲取指
              Text(
                  '你點擊右下角按鈕的次數:',
              ),
              Text(
                  '$_counter',               //綁定計數器的值
                            style: Theme.of(context).textTheme.display1,
                          ),
                        ],
                      ),
                    ),

//這個按鈕是在右下角的一個圖標
                    floatingActionButton: FloatingActionButton(
                      onPressed: _incrementCounter, //點擊+按鈕調用自增函數
                      tooltip: '增加',
                      child: Icon(Icons.add),
                    ),
                  );
              }
            }

3.Provider的使用

當我們的應用足夠簡單時,你可能並不需要狀態管理。但是隨着功能的增加,應用程序將會有幾十個甚至上百個狀態,你的應用狀態變得難以維護。Flutter實際上在一開始就爲我們提供了一種狀態管理方式,那就是StatefulWidget。但是我們很快發現,它正是造成上述問題的“罪魁禍首”。這時候,我們便迫切需要一個架構來幫助我們釐清這些關係,狀態管理框架應運而生。

總的說就是接口那使用Provider來實現動態佈局。讓我們來看看和上面代碼有什麼區別

        import 'package:flutter/material.dart';
        import 'package:provider/provider.dart';
        main() {
          runApp(MyApp(),);
        }

//這塊還是一樣使用StatelessWidget因爲home還是一樣調用類讓那個類裏面來返回佈局
        class MyApp extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
            // 這塊不一樣,使用了MultiProvider可以創建多個頂層共享數據。其中的數據就是 Counter()方法,這個方法在後面

//providers可以發現這個是個數組所以表示可以設置多個共享數據
            return MultiProvider(
              providers: [
                ChangeNotifierProvider(builder: (_) => Counter()),
              ],
              child: MaterialApp(
                title: "Provider示例",
                home: FirstPage(),
              ),
            );
          }
        }
        // 第一個頁面可以看出來使用的都是StatelessWidget非動態組件,和上面的代碼是不一樣的這個就是使用providers的不同點

//我的理解是因爲沒有使用setState所以不需要繼承動態組件
        class FirstPage extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
            return Scaffold(
              appBar: AppBar(
                title: Text("第一個頁面"),

//上面我說標題佈局可以設置下一頁就是在這塊
                actions: <Widget>[
                  FlatButton(
                      child: Text("下一頁"),
                      // 路由跳轉至第二頁
                      onPressed: () =>

//Navigator.push這個方法很常用,用於頁面之間跳轉
                          Navigator.push(context, MaterialPageRoute(builder: (context) {
                            return SecondPage();
                          })),
                  ),
                ],
              ),

//${Provider.of<Counter>(context).count}這塊就是獲取開頭設置的共享數據,看代碼可以看出來是調用Counter方法裏的獲取count方法
              body: Center(
                // 獲取計數器中的count值
                child: Text("${Provider.of<Counter>(context).count}"),
              ),
              floatingActionButton: FloatingActionButton(

// Provider.of<Counter>(context).increment()這個就是調取increment方法
                onPressed: () {
                  // 調用數據模型中的increment方法更改數據
                  Provider.of<Counter>(context).increment();
                },
                child: Icon(Icons.add),
                ),
              );
            }
          }
          // 第二個頁面
          class SecondPage extends StatelessWidget {
            @override
            Widget build(BuildContext context){
              return Scaffold(
                appBar: AppBar(
                  title: Text("第二個頁面"),
                ),
                body: Center(
                  // 獲取計數器中的count值
                  child: Text("${Provider.of<Counter>(context).count}"),
                ),
                floatingActionButton: FloatingActionButton(
                  onPressed: () {
                    // 調用數據模型中的increment方法更改數據
                    Provider.of<Counter>(context).increment();
                  },
                  child: Icon(Icons.add),
                ),
              );
            }
          }
          /**
          * 計數器類Counter即爲數據Model,實際上就是狀態。
          * Counter不僅存儲了數據,還包含了更改數據的方法,並暴露相關數據。
          * 使用mixin混入ChangeNotifier類,這個類能夠自動管理所有聽衆。
          * 當調用notifyListeners時,它會通知所有聽衆進行刷新
          */
          class Counter with ChangeNotifier {
            // 存儲數據
            int _count = 0;
            // 提供外部能夠訪問的數據
            int get count => _count;
            // 提供更改數據的方法
            void increment(){
              _count++;
              // 通知所有聽衆進行刷新
              notifyListeners();
            }
          }

4.最常用的重點來了http網絡請求

網絡請求有3種方法1.HTTP請求方式,2.HttpClient請求方式,3.Dio請求方式

前面的2個我就不寫了,直接使用完美的Dio請求方式就行:

Dio是一個強大的Dart Http請求庫,支持Restful API、FormData、攔截器、請求取消、Cookie管理、文件上傳/下載、超時、自定義適配器等

        .
        ├—— main.dart //主程序
        ├—— model      //數據模型層
        |    └—— good_list_model.dart //商品列表模型
        ├—— pages      //視圖層
        |    └—— good_list_page.dart   //商品列表頁面
        └—— service    //服務層
            └—— http_service.dart      //http請求服務

上面是描述項目文件有哪些。這是一個通過網絡請求獲取商品列表然後顯示list佈局

然後對上面文件一個一個描述。

main.dart文件程序入口

        import 'package:flutter/material.dart';
        import 'pages/good_list_page.dart';
        void main() => runApp(MyApp());
        class MyApp extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
            return MaterialApp(
              title: 'Dio請求',
              home: Scaffold(
                appBar: AppBar(
                  title: Text('Dio請求'),
                ),

//這塊和上面不一樣之前是 home: Scaffold裏的 Scaffold被放到類裏面現在是body裏的說明只需要主界面需要動態變化
                body: GoodListPage(),
              ),
            );
          }
        }

Dio請求封裝:

        import 'dart:io';
        import 'package:dio/dio.dart';
        import 'dart:async';
        // Dio請求方法封裝
        Future request(url, {formData}) async {
          try {
            Response response;
            Dio dio = Dio();
            dio.options.contentType = ContentType.parse('application/x-www-form-urlencoded');
            // 發起POST請求,傳入url及表單參數
            response = await dio.post(url, data: formData);
            // 成功返回
            if (response.statusCode == 200) {
              return response;
            } else {
              throw Exception('後端接口異常,請檢查測試代碼和服務器運行情況...');
            }
            } catch (e) {
              return print('error:::${e}');
            }
          }

 

接下來解析GoodListPage():代碼量會比較多

        import 'package:flutter/material.dart';
        import 'dart:convert';
        import '../model/good_list_model.dart';
        import '../service/http_service.dart';
        // 商品列表頁面

//可以發現使用的是StatefulWidget動態組件因爲他會在內部動態變化數據比如商品數據鋪在佈局裏
        class GoodListPage extends StatefulWidget {
          _GoodListPageState createState() => _GoodListPageState();
    }
    class _GoodListPageState extends State<GoodListPage> {
      // 初始化數據模型
      GoodListModel goodsList = GoodListModel([]);
      // 滾動控制,因爲是list所以需要添加滾動,這裏是把滾動初始化
      var scrollController = ScrollController();
      @override
      void initState() {
        super.initState();
        // 獲取商品數據
        getGoods();
      }
      // 獲取商品數據

//這個方法使用了async表示是異步方法,裏面請求數據使用的post請求
      void getGoods() async {
        // 請求url
        var url = 'http://127.0.0.1:3000/getDioData';
        // 請求參數:店鋪Id
        var formData = {'shopId': '001'};
        // 調用Dio封裝請求方法傳入url及表單數據;調用async函數必須使用await關鍵字
        await request(url, formData: formData).then((value) {
          // 返回數據進行Json解碼
          var data = json.decode(value.toString());
          // 打印數據
          print('商品列表數據Json格式:::' + data.toString());
          // 設置狀態刷新數據
          setState(() {
            // 將返回的Json數據轉換成Model這個model在後面會寫出來
            goodsList = GoodListModel.fromJson(data);
          });
        });
      }
      // 商品列表項  這個widget表示的是視圖佈局表示這個方法是返回佈局
      Widget _ListWidget(List newList, int index) {

//Container容器表示裏面有佈局用來返回
        return Container(

//界面內間距和這個背景顏色以及邊框
          padding: EdgeInsets.only(top: 5.0, bottom: 5.0),
          decoration: BoxDecoration(
              color: Colors.white,
              border: Border(
                  bottom: BorderSide(width: 1.0, color: Colors.black12),
              )),
          // 整體水平方向佈局內部嵌套一個垂直佈局Column
          child: Row(

//children: <Widget>表明Row水平佈局區間有哪些
            children: <Widget>[
              // 返回商品圖片佈局
              _goodsImage(newList, index),

//SizedBox這個創建了一個寬10的空間表示圖片和文字的間距
              SizedBox(
                  width: 10,
              ),
              // 右側使用垂直佈局
              Column(
                children: <Widget>[

//垂直佈局的2個text
                  _goodsName(newList, index),
                  _goodsPrice(newList, index),
                ],
              ),
            ],
          ),
        );
      }
      // 商品圖片佈局,上面有調用這個佈局
      Widget _goodsImage(List newList, int index) {
        return Container(

//設置佈局寬高內部是個圖片Image.network使用的是網絡圖片用network加載
          width: 150,
          height: 150,
          child: Image.network(newList[index].image,fit: BoxFit.fitWidth,),
        );
      }
      // 商品名稱
      Widget _goodsName(List newList, int index) {
        return Container(
          padding: EdgeInsets.all(5.0),
          width: 200,
          child: Text(
            newList[index].name,
            maxLines: 2,

//    overflow: TextOverflow.ellipsis,設置後文字不會超出屏幕
            overflow: TextOverflow.ellipsis,
            style: TextStyle(fontSize: 18),
          ),
        );
      }
      // 商品價格
      Widget _goodsPrice(List newList, int index) {
        return Container(
          margin: EdgeInsets.only(top: 20.0),
          width: 200,
          child: Row(
            children: <Widget>[
              Text(
                '價格:¥${newList[index].presentPrice}',
                style: TextStyle(color: Colors.red),
              ),
              Text(
                '¥${newList[index].oriPrice}',
              ),
            ],
          ),
        );
      }
      @override
      Widget build(BuildContext context) {
        // 通過商品列表數組長度判斷是否有數據
        if(goodsList.data.length > 0){
          return ListView.builder(
              // 滾動控制器
              controller: scrollController,
              // 列表長度
              itemCount: goodsList.data.length,
              // 列表項構造器
              itemBuilder: (context, index) {
                // 列表項,傳入列表數據及索引
                return _ListWidget(goodsList.data, index);
              },
            );
        }
        // 商品列表沒有數據時返回空容器
        return Container();
      }
    }

下面是mode類:

        // 商品列表數據模型
        class GoodListModel{
          // 狀態碼
          String code;
          // 狀態信息
          String message;
          // 商品列表數據
          List<GoodModel> data;
          // 構造方法,初始化時傳入空數組[]即可
          GoodListModel(this.data);
          // 通過傳入Json數據轉換成數據模型
          GoodListModel.fromJson(Map<String,dynamic> json){
            code = json['code'];
            message = json['message'];
            if(json['data'] != null){
              data = List<GoodModel>();
              // 循環迭代Json數據並將其每一項數據轉換成GoodModel
              json['data'].forEach((v){
                data.add(GoodModel.fromJson(v));
              });
          }
            }
          // 將數據模型轉換成Json
          Map<String,dynamic> toJson(){
            final Map<String,dynamic> data = Map<String,dynamic>();
            data['code'] = this.code;
            data['message'] = this.message;
            if(this.data != null){
              data['data'] = this.data.map((v) => v.toJson()).toList();
            }
            return data;
          }
        }
        // 商品信息模型
        class GoodModel{
          // 商品圖片
          String image;
          // 原價
          int oriPrice;
          // 現有價格
            int presentPrice;
            // 商品名稱
            String name;
            // 商品Id
            String goodsId;
            // 構造方法
            GoodModel({this.image,this.oriPrice,this.presentPrice,this.name,this.goodsId});
            // 通過傳入Json數據轉換成數據模型
            GoodModel.fromJson(Map<String,dynamic> json){
              image = json['image'];
              oriPrice = json['oriPrice'];
              presentPrice = json['presentPrice'];
              name = json['name'];
              goodsId = json['goodsId'];
            }
            // 將數據模型轉換成Json
            Map<String,dynamic> toJson(){
              final Map<String,dynamic> data = new Map<String,dynamic>();
              data['image'] = this.image;
              data['oriPrice'] = this.oriPrice;
              data['presentPrice'] = this.presentPrice;
              data['name'] = this.name;
              data['goodsId'] = this.goodsId;
              return data;
            }
          }

因爲太多所以分章節來寫,下一章見。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章