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;
            }
          }

因为太多所以分章节来写,下一章见。

发布了20 篇原创文章 · 获赞 2 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_28335347/article/details/105532290
今日推荐