入门Flutter基础学习笔记记录,踩坑!

Flutter基础学习

1、环境准备

1.首先到官网https://flutter.dev/docs/get-started/install/windows里下载Flutter的SDK

2.还得有Android studio的环境支持,因为flutter是依赖于Android sutdio运行的,

3.有勒Android studio的环境支持后,在Android studio里安装插件Flutter.

4.配置Flutter的系统环境,把Flutter的bin目录填入Path中即可。

5.在cmd中测试是否系统环境配置成功,flutter -h,并检测flutter环境是否全部配置完成

flutter doctor:flutter内置用于检测环境是否全部配置完成,只要检测出有X的情况,就是

环境还没有配置成功。

2、创建项目

2.1、Android Studio中编译

  • 1.下一步就是打包的包名,再一下不就是创建了(在这里可能会出现卡死状态,因为第一次 创建项目不来就要很久)

如果等的时间很久超过20分钟一般10分钟左右。

临时解决:

在flutter目录下两个cmd命令即可。

set PUB_HOSTED_URL=https://pub.flutter-io.cn

set FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

永久解决:

设置环境变量即可

name:PUB_HOSTED_URL,path:https://pub.flutter-io.cn

name:FLUTTER_STORAGE_BASE_URL,path:https://storage.flutter-io.cn

最后下载dio-cmd执行命令

flutter packages get

  • 2.禁用通过 Google Analytics 发送数据(以免国内网络连接失败问题)

执行flutter config --no-analytics

  • 3.因为怕有网络卡死的情况,我们还需要改下网络配置

3.1、打开创建好的项目中的android—>build.gradle

此页面修改两处

 repositories {
      
//        google()
//        jcenter()
     maven {
       url 'https://maven.aliyun.com/repository/google' }
     maven {
       url 'https://maven.aliyun.com/repository/jcenter' }
     maven {
       url 'http://maven.aliyun.com/nexus/content/groups/public' }
 }

3.2、打开D:\Flutter\flutter\packages\flutter_tools\gradle---->flutter.gradle,sdk中

修改移出

buildscript {
      
 repositories {
      
     //jcenter()
     // maven {
      
     //     url 'https://dl.google.com/dl/android/maven2'
     // }
     maven{
      
         url 'https://maven.aliyun.com/repository/jcenter'
     }
     maven{
      
         url 'http://maven.aliyun.com/nexus/content/groups/public'
     }
 }
 dependencies {
      
     classpath 'com.android.tools.build:gradle:3.1.2'
 }
}
  • 4.此刻去Android studio创建项目即可

2.2、使用VsCode来编写代码

当然环境配置啥的还是使用Android studio,vscode只是用来编写代码,因为这个比较轻量级。

  • 1.vscode中添加Flutter插件同时他会帮你把Dart插件也装上,如果没自己装上即可。
  • 2.准备一个bat打开虚拟机的文件(因为你总不能每次去Android里打开虚拟机吧!)
D:\AndroidSdk\emulator\emulator.exe -netdelay none -netspeed full -avd Pixel_2_API_28_noPB

注意:

1.前面的地址是由你安装AndroidSDK来决定的,不过有两个emulator.exe选择emulator文件夹里面的emulator.exe即可

-netdelay none:关闭网络延迟,-netspeed full全速运行

-avd 后面就是你虚拟机名,如果名字带有空格使用下划线(_)代替即可。

2.或点击vscode的右下角直接可以启动虚拟机

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CwGVgvd4-1610685529237)(E:\Typora_text\images\flutter5.png)]

  • 3.运行项目

1.cd到我们的项目地址

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uPT2AcQN-1610685529244)(E:\Typora_text\images\flutter2.png)]

2.运行

flutter run 

这里可能有点慢,这是在处理依赖。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UQcJGjC9-1610685529246)(E:\Typora_text\images\flutter3.png)]

出现这个代表运行成功了。

4.相关插件

  • Awesome Flutter Snippets:代码生成提示
  • Flutter+Dart:内核

2.3.使用命令

2.3.1、VsCode

//热更新:使用Dug运行可以实现ctrl+s热更新
r
//热重启
R
//显示网格
p
//Android和Ios视图切换
O

2.3.2、Android sutdio

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RlHWOxTO-1610685529250)(E:\Typora_text\images\flutter4.png)]

其余功能都自带

3、项目基本组件

3.1、Text Widget组件

//导入ui库
import 'package:flutter/material.dart';

//入口对象
void main() => runApp(MyApp());

//继承组件
class MyApp extends StatelessWidget {
     
  @override
  Widget build(BuildContext context) {
     
    //返回一个窗口然后里面填的是组件
    return MaterialApp(
      title: "Hello Word",
      home: Scaffold(
        appBar: AppBar(
          title: Text("标题"),
        ),
        body: Center(
            //主要注意
          child: Text(
            "Hello Word,I name is Tommy ,student Flutter jichu,so is luanxie yidian ",
            textAlign: TextAlign.center,
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
            style: TextStyle(
                fontSize: 25.0,
                color: Color.fromARGB(255, 122, 122, 122),
                decoration: TextDecoration.underline,
                decorationStyle: TextDecorationStyle.solid),
          ),
        ),
      ),
    );
  }
}

textAlign:代表文本对齐方式

maxLines:最多多少行

overflow:超出父容器的怎么处理

​ ellipsis:化为三点

style: 样式

​ TextStyle:字体样式

​ fontSize:大小(浮点型),color:颜色,decoration下划线,decorationStyle:下划线样式

3.2、 Container Widget组件

再主boody的Center里创建一个Container

Container 和div的思想一样,都是容器。

//导入ui库
import 'package:flutter/material.dart';

//入口对象
void main() => runApp(MyApp());

//继承组件
class MyApp extends StatelessWidget {
     
  @override
  Widget build(BuildContext context) {
     
    //返回一个窗口然后里面填的是组件
    return MaterialApp(
      title: "Hello Word",
      home: Scaffold(
        appBar: AppBar(
          title: Text("标题"),
        ),
        body: Center(
          child: Container(
            child: new Text("文本"),
            alignment: Alignment.center,
            width: 200,
            height: 200,
            color: Colors.blueAccent,
            padding: EdgeInsets.fromLTRB(10,0,0,0),
            margin: EdgeInsets.all(10),
            decoration: new BoxDecoration(
            gradient: const LinearGradient(colors: [
            Colors.blueAccent,
            Colors.lightGreenAccent,
            Colors.redAccent
           ]),
           border: Border.all(width: 2, color: Colors.amberAccent)),
          ),
        ),
      ),
    );
  }
}

注意是再Conter里有个child

  • child容器中有一个文本

  • alignment:对于容器里的组件的对齐方式-------->Alignment

  • width,height宽高(默认全屏)

  • color:容器的背景颜色----->Color来设置值

  • padding:内边距 ----->EdgeInsets来设置值—>fromLTRB四个方向

  • margin:外边距 --------->EdgeInsets来设置值---->all全部方向

  • decoration:设置背景渐变(与color冲突,其二选一)

    ​ BoxDecoration:背景渐变----->gradient设置颜色—>LinearGradient线性渐变

    ​ ----->border:设置容器边框

3.3、Image Widget组件

//导入ui库
import 'package:flutter/material.dart';

//入口对象
void main() => runApp(MyApp());

//继承组件
class MyApp extends StatelessWidget {
     
  @override
  Widget build(BuildContext context) {
     
    //返回一个窗口然后里面填的是组件
    return MaterialApp(
      title: "Hello Word",
      home: Scaffold(
        appBar: AppBar(
          title: Text("标题"),
        ),
        body: Center(
          child: Container(
            child: new Image.network(
              "https://www.baidu.com/img/dong_649e142b9088eaea580c3be34ac0bded.gif",
              fit: BoxFit.fill,
              color: Colors.amberAccent,
              colorBlendMode: BlendMode.darken,
            ),
            width: 500,
            height: 500,
          ),
        ),
      ),
    );
  }
}

  • 首先得选择显示图片的模式
    • network:以网络url显示
    • file:以手机内存的图片显示
    • aseet:以资源文件显示
    • fit:图片填充模式
      • BoxFit.fill:填充父容器大小
      • BoxFit.contain:剧中显示…
      • color:为图片叠加颜色-------->colorBlendMode:叠加颜色的模式

3.4、ListView Widget组件

3.4.1、常用语法

//导入ui库
import 'package:flutter/material.dart';

//入口对象
void main() => runApp(MyApp());

//继承组件
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //返回一个窗口然后里面填的是组件
    return MaterialApp(
      title: "Hello Word",
      home: Scaffold(
        appBar: AppBar(
          title: Text("标题"),
        ),
        body: Center(
          child: Container(child: new MyList(),height: 200,),
        ),
      ),
    );
  }
}

//万事皆组件
//List的组件,摘要出来就不会嵌套很深
class MyList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return new ListView(
      children: [
        new Container(
          width: 200,
          color: Colors.lightBlue,
        ),
        new Container(
          width: 200,
          color: Colors.lime,
        ),
        new Container(
          width: 200,
          color: Colors.lightGreenAccent,
        ),
        new Container(
          width: 200,
          color: Colors.purpleAccent,
        )
      ],
      scrollDirection: Axis.horizontal,
    );
  }
}

注意:在这里我提出来了List组件为了不嵌套太深。

  • children:listview中的组件
    • Container:listview中每一的样式。(这个也可以ListTile是自带的一个块级样式)
  • scrollDirection:设置横向与纵向---->Axis.horizontal横向,Axis.vertical纵向

3.4.2、动态ListView

//导入ui库
import 'package:flutter/material.dart';

//入口对象
void main() => runApp(MyApp(
    //传入一个参数
    items: new List.generate(
        10000, (i) => "Item $i is") //使用 => 语法代替for复制 $接收i
    ));

//继承组件
class MyApp extends StatelessWidget {
  //接收一个参数
  final List items;

  MyApp({Key key, @required this.items}); //此时items就是接收的参数

  @override
  Widget build(BuildContext context) {
    //返回一个窗口然后里面填的是组件
    return MaterialApp(
      title: "Hello Word",
      home: Scaffold(
        appBar: AppBar(
          title: Text("标题"),
        ),
        body: Center(
            child: new ListView.builder(
                itemCount: items.length,
                itemBuilder: (context, index) {
                 return new ListTile(title: new Text('${items[index]}'));
                })),
      ),
    );
  }
}

  • 基础Dart语言
  • 入口时传入一个参数即可List
  • MyApp({Key key, @required this.items})
    • @required:必须接收一的参数
  • ListView.builder:代表时动态的LIst
    • itemCount:接收的数据一共有多少条
    • itemBuilder:接收的数据以及显示样式
      • ‘${items[index]}’:显示数据

3.5、GrideView Widget组件

 Widget build(BuildContext context) {
    // TODO: implement build
    return new GridView(
//      设置GridView的参数
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        //列数
          crossAxisCount: 3,
          //横项的间隔
          mainAxisSpacing: 2.0,
          //列的间隔
          crossAxisSpacing: 2.0,
          //格子容器和内容的对比度
          childAspectRatio: 0.7),
//      样式
      children: [
        new Image.network(
            "http://tu.sixco.shop/upload/vod/20191121-1/5e86d23df4120f652c945c7093a9ef6b.jpg"),
        new Image.network(
            "http://tu.sixco.shop/upload/vod/20200405-1/230b33ba6f800b48c36146bab350b129.jpg"),
        new Image.network("https://tu.tianzuida.com/pic/upload/vod/2020-04-25/202004251587819345.jpg"),
        new Image.network(
            "http://tu.sixco.shop/upload/vod/20200414-1/52cd2142d8ef51f3fea14370d54ec7c1.jpg"),
        new Image.network(
            "http://tu.sixco.shop/upload/vod/20191119-1/77a250fba2e33f47e888a021d774d3ed.jpg"),
        new Image.network(
            "http://tu.sixco.shop/upload/vod/20191029-3/287d9ab512f979f872fc879e61d4ec26.jpg")
      ],
    );
  }
}

注意:这是组件的用法

  • gridDelegate:设置GridView的参数
    • crossAxisCount:列数
    • mainAxisSpacing:横项的间隔
    • crossAxisSpacing:列的间隔
    • childAspectRatio:格子容器和内容的对比度
  • children:数据样式

动态原理即可。

3.6、Flutter的布局组件

3.6.1、Row Widget水平布局

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context){
    return MaterialApp(
      title: '布局样式',
      home: Scaffold(
        appBar: new AppBar(title: new Text("水平方向布局",)),
//        Row横向布局
        body: new Row(
          children: [
//            普通
            new RaisedButton(onPressed: (){},color: Colors.purpleAccent,child: new Text('按钮1'),),
//           Expanded权重,占全距离
           Expanded(child:  new RaisedButton(onPressed: (){},color: Colors.blueAccent,child: new Text('按钮2'),),),
            new RaisedButton(onPressed: (){},color: Colors.lightGreenAccent,child: new Text('按钮3'),)
          ],
        ),
      ),
    );
  }

}

主要注意的是Expanded权重

children:容器放入布局中的组件即可。

3.6.2、Column Widget 垂直布局

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
     
  @override
  Widget build(BuildContext context) {
     
    return MaterialApp(
      title: '布局样式',
      home: Scaffold(
          appBar: new AppBar(
              title: new Text(
            "水平方向布局",
          )),
//        Row横向布局
          body: Center(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.center, //横轴对齐
              mainAxisAlignment: MainAxisAlignment.center, //纵轴对齐
              children: <Widget>[
                Text("第一段"),
                Expanded(
                  child: Text("第二段"),
                ),
                Text("第三段")
              ],
            ),
          )),
    );
  }
}

Center:剧中块

注意:

  • crossAxisAlignment:横轴对齐
  • mainAxisAlignment:纵轴对齐
  • children:垂直布局中的容器
    • Expanded:同样也是权重

3.6.3、Stack Widget 重叠布局

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
//  实例化一个重叠布局为变量
  var stack = new Stack(
//    重叠布局的优先级
    alignment: const FractionalOffset(0.5, 0.8),
//    容器
    children: [
//      圆角图片
      new CircleAvatar(
        backgroundImage: new NetworkImage("https://www.baidu.com/img/bd_logo1.png"),
        radius: 100,
      ),
      new Container(
        decoration: new BoxDecoration(
          color: Colors.lightGreenAccent,
        ),
        padding: EdgeInsets.all(5),
        child: Text("文字"),
      )
     new Positioned(
        child: new Text("定位布局"),
        top: 10,
        left: 60,
      )
    ],
  );
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '布局样式',
      home: Scaffold(
          appBar: new AppBar(
              title: new Text(
            "水平方向布局",
          )),
//        Row横向布局
          body: Center(
          child: stack,
          )),
    );
  }
}

注意:这里我们实例化了重叠布局,然后再放入body中的

  • alignment:重叠的优先级
  • children:容器
  • CircleAvatar:圆角图片

注意:这里只能控制2个的字布局,如果有第三个,可以使用定位布局

  • Positioned:定位布局,直接控制像素

3.6.4、card + ListTile 卡片布局+块级(图标 + 文字+把标题,固定好的)

//  实例化一个重叠布局为变量
  var card = new Card(
    child: Column(
      children: [
        ListTile(
          title: new Text("第一条数据",style: TextStyle(fontWeight: FontWeight.w300),),
          subtitle: Text("副标题1"),
          leading: Icon(Icons.accessible_forward),
        ),
        new Divider(),
        ListTile(
          title: new Text("第二条数据",style: TextStyle(fontWeight: FontWeight.w300),),
          subtitle: Text("副标题2"),
          leading: Icon(Icons.accessible_forward),
        ),
        new Divider(),
        ListTile(
          title: new Text("第三条数据",style: TextStyle(fontWeight: FontWeight.w300),),
          subtitle: Text("副标题3"),
          leading: Icon(Icons.accessible_forward),
        ),
        new Divider(),
      ],
    ),
  );
  • card布局主要是带有阴影,它与容器差不多
  • ListTile :块级(图标 + 文字+把标题,固定好的)
  • children:card的容器
    • ListTile:放入一个块级
      • title:主标题
      • subtitle:副标题
      • leading:主图标
    • Divider:分割线

3.7、导航跳转

3.7.1、普通跳转

import 'package:flutter/material.dart';

//设置页面为home主页启动页
void main() => runApp(MaterialApp(
      title: "导航1",
      home: MyApp(),
    ));

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text("导航页面"),
      ),
      body: Center(
        child: RaisedButton(
          child: Text("查看第二个页面"),
          onPressed: () {
            //点击事件
//            Navigator导航启动器
            Navigator.push(
//              MaterialPageRoute跳入哪个子页面
                context, MaterialPageRoute(builder: (context) => new ToView()));
          },
        ),
      ),
    );
  }
}

//静态组件:第二个页面
class ToView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(title: Text("第二个页面")),
      body: Center(
        child: RaisedButton(
          child: Text("返回"),
          onPressed: () {
            //点击事件
//            返回上一个页面
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}

注意:这里所有的new谷歌建议不适用new 直接使用即可

  • 再main里要注定home主页是哪个组件

  • onPressed:点击事件

  • Navigator.push:启动一个跳转页面

    • MaterialPageRoute:路由。
      • ToView:第二个组件(也就是页面)
  • Navigator.pop:传入上下文返回上一个页面

3.7.2、带有参数跳转界面

import 'package:flutter/material.dart';

//准备一个对象
class Product {
  final String title;
  final String desc;

  //生成构造方法
  Product(this.title, this.desc);
}

//设置页面为home主页启动页
void main() => runApp(MaterialApp(
      title: "导航1",
      home: MyApp(
          products:
              List.generate(20, (i) => Product("商品:$i", "详细商品$i"))), //传入参数
    ));

class MyApp extends StatelessWidget {
  //接收的参数
  final List products;

  MyApp({Key key, @required this.products}); //接收参数
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text("导航页面"),
      ),
      body: Center(
        child: ListView.builder(
            itemCount: products.length,
            itemBuilder: (context, index) {
              return ListTile(
                title: Text(products[index].title),
                onTap: () {
                  //点的单个列表事件
                  //跳转并传入参数
                  Navigator.push(
                      context,
                      MaterialPageRoute(
//                传入参数
                          builder: (context) =>
                              ToView(product: products[index])));
                },
              );
            }),
      ),
    );
  }
}

//静态组件:第二个页面
class ToView extends StatelessWidget {
//  接收参数
  Product product;

  ToView({Key key, @required this.product}); //接收参数
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(title: Text("${product.title}")),
      body: Center(
        child: Text("${product.desc}")
      ),
    );
  }
}

注意:在这里我们自定义了一个Product对象。

  • 首先传入了一个List给了第一个主界面
  • Navigator.push:跳转页面
    • MaterialPageRoute:设置路由
      • ToView(product: products[index])));:跳转的页面中代入参数,以map的方式传入
  • 同样以: ToView({Key key, @required this.product}); //接收参数
  • 显示参数使用${}即可显示

3.7.3、返回页面带参数

import 'package:flutter/material.dart';


//设置页面为home主页启动页
void main() => runApp(MaterialApp(title: "导航1", home: MyApp()));

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(title: Text("找人"),),
      body: Center(
        child: MyButton(),
      ),
    );
  }
}
//如果使用Scaffold就不能把该组件嵌套到child中
class MyButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return RaisedButton(
      child: Text("要电话号码"),
      onPressed: (){
        //点击事件
        _navigateTo(context);
      },
    );
  }

}
//  准备一个方法去往页面
_navigateTo(BuildContext context ) async{
  final res = await Navigator.push(context, MaterialPageRoute(
      builder: (context)=>ToView()
  ));
//    底部对话框
  Scaffold.of(context).showSnackBar(SnackBar(content: Text('$res'),));
}

//静态组件:第二个页面
class ToView extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(title: Text("人群")),
      body: Center(
        child: Column(
          children: [
            RaisedButton(
              child:Text("张三:12321421"),
              onPressed: (){
//                点击事件
              Navigator.pop(context,"张三:12321421");
              },
            ),
            RaisedButton(
              child:Text("李四:21412412"),
              onPressed: (){
//                点击事件
                Navigator.pop(context,"李四:21412412");
              },
            ),
          ],
        ),
      ),
    );
  }
}

注意:

  • 如果要使用Scaffold.of他的调用组件不能嵌套,需要提出来(MyButtom)。
  • Navigator.pop(context,“张三:12321421”):返回页面并携带参数
  • 再跳转页面时返回的对象就时返回页面的携带值。(最好只加上async异步请求)

4、静态资源的管理

4.1、静态图片

  • 首先修改pubspec.yml文件
#添加
  assets:
  #文件路径:根目录下的images
  - images/
  • 使用
new Image.asset("images/img.png"),

4.2、修改logo图片以及软件名

  • 修改软件名

    • android:android—>src–>main—>res—>AndroidManifest.xml里修改label

    • ios:ios—>Runner–>info.plist–>

      	CFBundleName
      	flutter_dome02
      
  • 修改logo图标

    • 把自己的logo图片修改名为:ic_launcher.png替换android—>src–>main—>res---->mipmap所有文件即可

      当然,每个文件都有每个文件的大小。如果你有其他办法也可以使用

5、网络访问DIO使用

这里推荐一个easy-mock这个可以创建模拟接口给你测试(全网使用)

官网:https://www.easy-mock.com/login

功能:

  • 创建url
  • 创建接口get、post

5.1、导入DIO包

这里考虑到没有,然后有很多坑

  • 首先在pubspec.yml 文件里

    • dependencies:上线状态

      dependencies:
        flutter:
          sdk: flutter
      
        # The following adds the Cupertino Icons font to your application.
        # Use with the CupertinoIcons class for iOS style icons.
        cupertino_icons: ^0.1.2
        dio: ^3.0.0
      

      注意yml时空格来决定格式的,dio:(这里有个空格) ^参数

    • dev_dependencies:开发状态

  • 在dart文件中导入

    import 'package:dio/dio.dart';
    
  • 与创建项目一致,打开该项目的cmd控制台

  • 输入命令跨网

    • set PUB_HOSTED_URL=https://pub.flutter-io.cn
    • set FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
  • 在输入命令下载dio

    • flutter packages get

5.2、get请求

//  准备一个测试网络的方法
  Future getHttp() async {
    try {
      Response response;
      response = await Dio().get("https://www.easy-mock.com/mock/5ead226ca9af2438fc69eeb0/tommy/getData
");
      return print(response);
    } catch (e) {
      return print(e);
    }
  }

注意:

  • https://www.easy-mock.com/mock/5ead226ca9af2438fc69eeb0/tommy/getData这时eays-mock里自定义的

5.3、Post请求

//  准备一个测试网络的方法
  Future getHttp() async {
    try {
      Response response;
      var data = {"name":"fdsa"};  //准备参数对象
      //这个接口时easy-mock里自定义的post接口
      response = await Dio().post("https://www.easy-mock.com/mock/5ead226ca9af2438fc69eeb0/tommy/setData",
//      填入参数对象
      data: data);
      return print(response);
    } catch (e) {
      return print(e);
    }
  }

注意

  • 接口同样也是asys-mock中的
  • data是参数对象

5.4、获取后的json数据解析

  • 导入包:import ‘dart:convert’; //引入json解析插件,具体方法参考<<导入DIO包>>
//          使用json把字符串转为json对象
var data = json.decode(getHttp().data.toString());

6、打包为apk

注意这里只有Android打包

6.1、打包前得生成签名文件

  • 找到keytool.exe位置

    flutter doctor -v
    

    获取一个java地址

    D:\AndroidStudio\jre\bin\java

    当然你的可能不一样(这个是Android studio自带的jdk目录)

    进入bin目录,

  • 运行官方提供的命令

keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key

注意:

  • 生产的签名文件在bin目录下

修改完成后,他会有一堆提问按提示输入即可,CN那个问题给一个y即可。

6.1、生成apk

  • 首先得到android文件夹里创建一个文件夹key.properties,并存入钥匙。
storePassword=123456
keyPassword=123456
keyAlias=key
storeFile=D:/key.jks
  • 配置key (1),修改android/app/build.gradle文件
def keystorePropertiesFile = rootProject.file("key.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
//-------
//android {

在android{前添加三行}(可能爆红,没关系)

  • 配置key(2)
//buildTypes {
//    release {
//        signingConfig signingConfigs.debug
//    }
//}
//-----上替换为下
signingConfigs {
    release {
        keyAlias keystoreProperties['keyAlias']
        keyPassword keystoreProperties['keyPassword']
        storeFile file(keystoreProperties['storeFile'])
        storePassword keystoreProperties['storePassword']
    }
}
buildTypes {
    release {
        signingConfig signingConfigs.release
    }
}

将上边代码替换为下边。

  • 最后生成apk
//命令
flutter build apk

生成后的地址

build---->app—>outpputs---->apk---->release---->app-release.apk

如果没有release文件夹可能时没签名成功。

6、实战电商笔记

6.1、布局总架构(底部导航)

  • 进入main入口dart
import 'package:flutter/material.dart';
import 'package:flutter_electricity/pages/index_page.dart';

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

class MyApp extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return MaterialApp(
      title: "电商管理",
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: Colors.pink
      ),
      home: Scaffold(
//        appBar: new AppBar(title: Text("主页"),),
        body: Center(
          child: IndexPage(),
        ),
      ),
    );
  }

}

注意两种类型:

import ‘package:flutter/material.dart’;------>这个是Android样式差不多

import ‘package:flutter/cupertino.dart’;------> 这个是ios样式差不多

import ‘package:flutter_electricity/pages/index_page.dart’------> 导入了主页页面.

  • 准备四个页面

    //适合Android的布局
    import 'package:flutter/material.dart';
    
    void main() => runApp(CartPage());
    
    class CartPage extends StatelessWidget{
      @override
      Widget build(BuildContext context) {
        // TODO: implement build
        return Scaffold(
          backgroundColor: Colors.pink,
            body: Center(
              child: Text("购物车"),
            ),
          );
      }
    
    }
    

    内容自定义

  • 架构业 index_page.dart

//适合Android的布局
import 'package:flutter/material.dart';
//适合ios的布局
import 'package:flutter/cupertino.dart';
//导入页面
import 'package:flutter_electricity/pages/cart_page.dart';
import 'package:flutter_electricity/pages/category_page.dart';
import 'package:flutter_electricity/pages/home_page.dart';
import 'package:flutter_electricity/pages/member_page.dart';

//动态布局,fragment
class IndexPage extends StatefulWidget {
  @override
  _IndexPageState createState() => _IndexPageState();
}

class _IndexPageState extends State {
//  准备图标与文字
  final List bottmTabs = [
    BottomNavigationBarItem(icon: Icon(CupertinoIcons.home), title: Text("首页")),
    BottomNavigationBarItem(
        icon: Icon(CupertinoIcons.search), title: Text("分类")),
    BottomNavigationBarItem(
        icon: Icon(CupertinoIcons.shopping_cart), title: Text("购物车")),
    BottomNavigationBarItem(
        icon: Icon(CupertinoIcons.profile_circled), title: Text("会员中心")),
  ];

//  准备fragment页面
  final List tabBodies = [HomePage(), CateGoryPage(), CartPage(), MembarPage()];

//选择页面的索引
  int currentIndex = 0;

//选择的页面
  var currentPage;

//初始化
  @override
  void initState() {
    //    底部对话框
    currentPage = tabBodies[currentIndex];
    super.initState();
  }

//布局
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
//      设置背景也是
      backgroundColor: Color.fromRGBO(244, 245, 245, 1.0),
      body: currentPage,
//      设置底部按钮
      bottomNavigationBar: BottomNavigationBar(
//        设置一种类型
        type: BottomNavigationBarType.fixed,
//        设置当前页面索引
        currentIndex: currentIndex,
//        设置所有的页面对象
        items: bottmTabs,
        onTap: (index) {
          setState(() {
//            改变页面索引
            currentIndex = index;
            currentPage = tabBodies[currentIndex];
          });
        },
      ),
    );
  }
}

注意:导入对象

  • StatefulWidget:动态布局入口Fragment入口
  • _IndexPageState:动态组件
    • 首先准备BottomNavigationBarItem的list来存入图片与文字,当然flutter里就有自带图标BottomNavigationBarItem(icon: Icon(CupertinoIcons.home)
    • 准备list来存入所有的Fragment子页面(当然这里只要填入页面中的入口类名即可。)
    • 准备一个索引,用于存入当前位置currentIndex。
    • 准备一个对象,用于存入页面currentPage。
    • 初始化页面的回调中initState—>初始化最先显示的页面
    • 其次就时布局
      • 注意一定有一个body把currentPage当前界面存入
      • bottomNavigationBar:底部导航,填入类型,当前页面索引,所有页面对象,以及实现onTap点击单个来切换页面。

6.2、构建网络请求框架

6.2.1、测试DIO的get请求

  • 导入DIO包

这里考虑到没有,然后有很多坑

  • 首先在pubspec.yml 文件里

    • dependencies:上线状态

      dependencies:
        flutter:
          sdk: flutter
      
        # The following adds the Cupertino Icons font to your application.
        # Use with the CupertinoIcons class for iOS style icons.
        cupertino_icons: ^0.1.2
        dio: ^3.0.0
      

      注意yml时空格来决定格式的,dio:(这里有个空格) ^参数

    • dev_dependencies:开发状态

  • 在dart文件中导入

    import 'package:dio/dio.dart';
    
  • 与创建项目一致,打开该项目的cmd控制台

  • 输入命令跨网

    • set PUB_HOSTED_URL=https://pub.flutter-io.cn
    • set FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
  • 在输入命令下载dio

    • flutter packages get
  • home_page.dart
//适合Android的布局
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';

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

class HomePage extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    getHttp();
    return Scaffold(
      body: Center(
        child: Text("首页"),
      ),
    );
  }
//  准备一个测试网络的方法
void getHttp() async{
    try{
      Response response;
      response = await Dio().get("https://v1.hitokoto.cn/");
      return print(response);
    }catch(e){
      return print(e);
    }
}
}

Response 和后台服务一样,是获取请求回来参数对象

6.2.2、网络框架

这样能减少项目开发的维护成本,以及统一性.

  • 创建一个servce_url(用于管理url以及api调用)
//封装url
const servceUrl = "http://106.13.185.176:7777/manage/"; //准备一个url
const servcePath={
  'home':servceUrl+"getData"  //获取主页api
};

注意:这里我使用的接口是自己第一个接口

  • 创建service_method.dart(用于管理调用api接口的方法)
//封装访问接口方法
//导入习惯访问插件
import 'package:dio/dio.dart';
import 'dart:async';
import 'dart:io';
import 'package:flutter_electricity/config/servce_url.dart';
//打印
const TAG_OK = "正在调用接口:------------->";
const TAG_ERROR = "服务器出现问题:------------->";


//获取主页内容
Future getHomePageData() async{
  try{
    print(TAG_OK+(servcePath['home']));
    Response response ;
    Dio dio = new Dio();
    response = await dio.post(servcePath["home"]);
    return response;
  }catch(e){
    return print(TAG_ERROR+e);
  }
}

注意:

  • 导入相关所有插件
  • 调用方法返回Futuer并用上异步请求
  • 调用测试
//  准备一个测试网络的方法
  Future getHttp()  {
    getHomePageData().then((val){
      setState(() {
//        这个方法时动态布局使用---->修改变量时,他会改变布局样式。
      textValue = val.toString();
      });
    });
  }

注意:

  • 返回的Future使用then来解析数据

  • setState方法是动态布局中使用的,

    • 它实现了双向绑定数据效果,修改数据,页面值也会修改。

6.3、主页开发

6.3.1、主页轮播图编写–>插件:flutter_swiper

  • 导入组件:import ‘package:flutter_swiper/flutter_swiper.dart’; //导入轮播图插件,需要再pubspec.yml里
  • home_page.dart
//适合Android的布局
import 'package:flutter/material.dart';
import 'package:flutter_swiper/flutter_swiper.dart';  //导入轮播图插件
import 'dart:convert';    //引入json解析插件

//导入极客的头文件
import 'package:flutter_electricity/config/httpHead.dart';
import 'package:flutter_electricity/servceFun/servce_method.dart';

class HomePage extends StatefulWidget {
  @override
  State createState() {
    // TODO: implement createState
    return HomePageState();
  }
}

class HomePageState extends State {
//  声明数据:一定记得实例化
  TextEditingController inputValue = TextEditingController();
  String textValue = "";

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(title: Text("首页"),),
//      这个组件可以自动根据异步请求来渲染页面。
      body: FutureBuilder(
//       一个异步方法
        future: getHomePageData(),
//      构造器
      builder: (context,snapshot){
        if(snapshot.hasData){ //里面是否存在值
//          使用json把字符串转为json对象
          var data = json.decode(snapshot.data.toString());
//          因为这里需要的是List所以需要将json转为list
//          List swiper = (data['data']['slides'] as List).cast();
//          这里因为没有接口所以,自己模拟数据
          List listData = new List();
          listData.add("http://a1.att.hudong.com/05/00/01300000194285122188000535877.jpg");
          listData.add("http://a4.att.hudong.com/20/62/01000000000000119086280352820.jpg");
          listData.add("http://file02.16sucai.com/d/file/2014/0829/372edfeb74c3119b666237bd4af92be5.jpg");
          //返回我们的静态轮播图组件
          return SwiperWidget(swiperDataList: listData,);
        }else{
          return Text("正在加载...");
        }
      },
      ),
    );
  }

//  测试输入框
  void getInput(context) {
    showDialog(
        context: context,
        builder: (context) => AlertDialog(
              title: Text("文本框:" + inputValue.text.toString()),
            ));
  }

//  准备一个测试网络的方法
  Future getHttp() {
    getHomePageData().then((val) {
      setState(() {
//        这个方法时动态布局使用---->修改变量时,他会改变布局样式。
        textValue = val.toString();
      });
    });
  }
}

//轮播图,静态组件
class SwiperWidget extends StatelessWidget {
//  接收一个list的参数
  final List swiperDataList;
  SwiperWidget({Key key, this.swiperDataList}) : super(key: key); //传入一个参数

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      height: 250,
      child: Swiper(
//        轮播图内容
        itemBuilder: (BuildContext context,int index){
          return Image.network("${swiperDataList[index]}" ,fit: BoxFit.fill,);
        },
//        个数
        itemCount: swiperDataList.length,
//        是否有移动小图标
        pagination: SwiperPagination(),
//        运行自动播放
        autoplay: true,
      ),
    );
  }
}

注意:

  • FutureBuilder: 这个组件可以自动根据异步请求来渲染页面。
    • future:需要给一个异步请求方法
    • builder:构造方法,填入相应的逻辑
  • Swiper:轮播组件
    • itemBuilder:构造方法,轮播的容器,字布局
    • itemCount:轮播字布局的个数
    • pagination:是否有小图标—>SwiperPagination()构造
    • autoplay:是否自动播放

6.3.2、使用flutter_screenutil适配布局

  • 导入组件:import ‘package:flutter_screenutil/flutter_screenutil.dart’; //导入适配组件—》需要再pubspec.yml里添加
  • index_page.dart
//布局
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    //在主入口初始化screenutil适配器
    ScreenUtil.init(context,width: 750,height: 1334); //初始宽高
    return Scaffold(
//      设置背景也是
      backgroundColor: Color.fromRGBO(244, 245, 245, 1.0),

注意一般初始化插件值初始化一次,所以放在第一次进入的界面

  • home_page.dart
return Container(
    height: ScreenUtil().setHeight(250),
    width: ScreenUtil().setWidth(750),
    child: Swiper(
        //        轮播图内容
        itemBuilder: (BuildContext context,int index){
            return Image.network("${swiperDataList[index]}" ,fit: BoxFit.fill,);
        },
        //        个数
        itemCount: swiperDataList.length,
        //        是否有移动小图标
        pagination: SwiperPagination(),
        //        运行自动播放
        autoplay: true,
    ),
);

注意:

  • 设置高宽以及字体大小

    • height: ScreenUtil().setHeight(250),
    • width: ScreenUtil().setWidth(750)
    • fontSize: ScreenUtil().setSp(28,false)
      • 这里的false参数书代表不会跟着系统字体改变

6.3.3、主页网格菜单栏布局

  • home_page.dart
//适合Android的布局
import 'package:flutter/material.dart';
import 'package:flutter_swiper/flutter_swiper.dart'; //导入轮播图插件
import 'dart:convert'; //引入json解析插件
import 'package:flutter_screenutil/flutter_screenutil.dart'; //导入适配组件

//导入极客的头文件
import 'package:flutter_electricity/config/httpHead.dart';
import 'package:flutter_electricity/servceFun/servce_method.dart';

class HomePage extends StatefulWidget {
  @override
  State createState() {
    // TODO: implement createState
    return HomePageState();
  }
}
//    设置一个模拟网格菜单项数据的对象
class Navg{
  String img;
  String name;
  Navg(String img,String name);
}
class HomePageState extends State {
//  声明数据:一定记得实例化
  TextEditingController inputValue = TextEditingController();
  String textValue = "";
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(title: Text("首页"),),
//      这个组件可以自动根据异步请求来渲染页面。
      body: FutureBuilder(
//       一个异步方法
        future: getHomePageData(),
//      构造器
        builder: (context, snapshot) {
          if (snapshot.hasData) { //里面是否存在值
//          使用json把字符串转为json对象
            var data = json.decode(snapshot.data.toString());
//          因为这里需要的是List所以需要将json转为list
//          List swiper = (data['data']['slides'] as List).cast();
//          这里因为没有接口所以,自己模拟数据----》轮播图
            List listData = new List();
            listData.add(
                "http://a1.att.hudong.com/05/00/01300000194285122188000535877.jpg");
            listData.add(
                "http://a4.att.hudong.com/20/62/01000000000000119086280352820.jpg");
            listData.add(
                "http://file02.16sucai.com/d/file/2014/0829/372edfeb74c3119b666237bd4af92be5.jpg");
//            模拟数据------》网格布局中的菜单项
            List navgatorList = new List();
            navgatorList.add({"img":"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1962359944,4033386090&fm=26&gp=0.jpg","name":"白酒"});
            navgatorList.add({"img":"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=2644613255,429458660&fm=26&gp=0.jpg","name": "红酒"});
            navgatorList.add({"img":"https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=123433492,672306588&fm=11&gp=0.jpg","name": "啤酒"});
            navgatorList.add({"img":"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1971380632,2889601273&fm=11&gp=0.jpg", "name":"绿酒"});
            navgatorList.add({"img":"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2254386225,705619148&fm=26&gp=0.jpg", "name":"黑酒"});
            navgatorList.add({"img":"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1336046231,1070862462&fm=26&gp=0.jpg", "name":"酒神"});
            navgatorList.add({"img":"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3925608220,2911772755&fm=26&gp=0.jpg","name": "酒圣"});
            navgatorList.add({"img":"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2809691443,3777995480&fm=26&gp=0.jpg", "name":"铪酒"});
            navgatorList.add({"img":"https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1899337755,3087141316&fm=26&gp=0.jpg", "name":"米酒"});
            navgatorList.add({"img":"https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1235513397,588910321&fm=26&gp=0.jpg","name": "源酒"});
            //返回我们的静态轮播图组件
            return Column(
              children: [
              SwiperWidget(swiperDataList: listData,),
              TopNavigator(navigatorList: navgatorList,),
                Image.network("https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1962359944,4033386090&fm=26&gp=0.jpg")
              ],
            );
          } else {
            return Text("正在加载...", style: TextStyle(
//            fontSize: ScreenUtil().setSp(28,false)
            ),);
          }
        },
      ),
    );
  }

//  测试输入框
  void getInput(context) {
    showDialog(
        context: context,
        builder: (context) =>
            AlertDialog(
              title: Text("文本框:" + inputValue.text.toString()),
            ));
  }

//  准备一个测试网络的方法
  Future getHttp() {
    getHomePageData().then((val) {
      setState(() {
//        这个方法时动态布局使用---->修改变量时,他会改变布局样式。
        textValue = val.toString();
      });
    });
  }
}

//轮播图,静态组件
class SwiperWidget extends StatelessWidget {
//  接收一个list的参数
  final List swiperDataList;

  SwiperWidget({Key key, this.swiperDataList}) : super(key: key); //传入一个参数

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
//    使用ScreenUtil适配大小
    print('设备像素密度:${ScreenUtil.pixelRatio}');
    print('设备的高:${ScreenUtil.screenHeightDp}');
    print('设备的宽:${ScreenUtil.screenWidthDp}');
    return Container(
      height: ScreenUtil().setHeight(333),
      width: ScreenUtil().setWidth(750),
      child: Swiper(
//        轮播图内容
        itemBuilder: (BuildContext context, int index) {
          return Image.network("${swiperDataList[index]}", fit: BoxFit.fill,);
        },
//        个数
        itemCount: swiperDataList.length,
//        是否有移动小图标
        pagination: SwiperPagination(),
//        运行自动播放
        autoplay: true,
      ),
    );
  }
}


//网格布局
class TopNavigator extends StatelessWidget {
  final List navigatorList;
  TopNavigator({Key key, this.navigatorList}) :super(key: key);
//  设置一个字布局,
  Widget _gridViewIntemUI(BuildContext context, List item,int index) {
    return InkWell(
      onTap: () {
        print(("点击了导航"));
      },
      child: Column(
        children: [
          Image.network(item[index]['img'], width: ScreenUtil().setWidth(95),),
          Text(item[index]['name'])
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
        height: ScreenUtil().setHeight(320),
        padding: EdgeInsets.all(3.0),
        child: GridView.builder(
          itemCount: navigatorList.length,
          itemBuilder: (BuildContext context,int index){
            return _gridViewIntemUI(context, navigatorList,index);
          },
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 5
          ),
        )
    );
  }

}

注意:

  • 获取数据返回GridView
    return Column(
              children: [
              SwiperWidget(swiperDataList: listData,),
              TopNavigator(navigatorList: navgatorList,),	//返回网格菜单栏
  • 准备一个字布局,返回Widget就行
//  设置一个字布局,
  Widget _gridViewIntemUI(BuildContext context, List item,int index) {
    return InkWell(
      onTap: () {
        print(("点击了导航"));
      },
      child: Column(
        children: [
          Image.network(item[index]['img'], width: ScreenUtil().setWidth(95),),
          Text(item[index]['name'])
        ],
      ),
    );
  }
  • 在网格布局中设置动态字布局
child: GridView.builder(
    //设置个数
    itemCount: navigatorList.length,
    itemBuilder: (BuildContext context,int index){
        return _gridViewIntemUI(context, navigatorList,index);
    },
    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 5
    ),
)

itemBuilder中返回动态组件就行。

gridDelegate:里设置类型

6.3.4、主页广告区

  • home_page.dart
class AdBanner extends StatelessWidget{
  String imgUrl ;
  AdBanner({Key key,this.imgUrl}):super(key:key);
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      height: ScreenUtil().setHeight(100),
      width: ScreenUtil().setWidth(750),
      child: Image.network(imgUrl,fit: BoxFit.fill,),
    );
  }

}

添加一个图片组件即可

6.3.5、主页点击拨打电话功能

  • 导入插件:import ‘package:url_launcher/url_launcher.dart’; //导入拨打电话获取发短信插件

    • 需在pubspec.yml里添加版本
  • home_page.dart

class LeaderPhone extends StatelessWidget{
  String imgUrl;
  String phone;
  LeaderPhone({Key key,this.imgUrl,this.phone}):super(key:key);

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      height: ScreenUtil().setHeight(200),
      width: ScreenUtil().setWidth(750),
//      水波布局(一半亮一半暗)      height: ScreenUtil().setHeight(100),
      child: InkWell(
        onTap: _launder,
        child: Image.network(imgUrl, fit: BoxFit.fill,),
      )
    );
  }
//  拨打电话方法
  void _launder() async{
    String uri = 'tel:'+phone;
    if(await canLaunch(uri)){ //Launch内置方法判断是否正确uri
      await launch(uri);  //如果正确,就去往
    }else{
      throw "------------>Error:launcher的uri不正确";
    }
  }
}

注意:

  • 主要组件就一个图片即可
  • url_launcher用法
    • 准备一个url
      • 电话:tel:电话
      • 网页:http:----
      • 短信:sum:
    • canLaunch:用来判断uri是否正确
    • launch:去往uri地址。

6.3.6、主页商品推荐开发

  • home_page
//商品推荐组件
class Recommend extends StatelessWidget {
//  准备一个数据
  final List recommendList;

  Recommend({Key key, this.recommendList}) : super(key: key);

//子组件---》标题
  Widget _titleItem() {
    return Container(
//      剧中靠左
      alignment: Alignment.centerLeft,
      padding: EdgeInsets.only(left: 10),
      decoration: BoxDecoration(
          color: Colors.white,
          border: Border(bottom: BorderSide(width: 0.5, color: Colors.grey))),
      child: Text(
        "商品推荐",
        style: TextStyle(color: Colors.blueAccent),
      ),
    );
  }

//子组件--->ListView的子组件
  Widget _ListViewItme(index) {
    return InkWell(
      onTap: () {},
      child: Container(
        height: ScreenUtil().setHeight(330),
        width: ScreenUtil().setWidth(250),
        padding: EdgeInsets.all(2),
        decoration: BoxDecoration(
            color: Colors.white,
            border: Border(left: BorderSide(width: 0.5, color: Colors.grey))),
        child: Column(
          children: [
            Image.network(recommendList[index]['img']),
            Text("¥${recommendList[index]['money']}"),
            Text(
              "¥${recommendList[index]['price']}",
//              设置删除线
              style: TextStyle(
                  decoration: TextDecoration.lineThrough, color: Colors.grey),
            )
          ],
        ),
      ),
    );
  }

//  子组件---ListView横向
  Widget _recommendList() {
    return Container(
      height: ScreenUtil().setHeight(350),
      child: ListView.builder(
        itemCount: recommendList.length,
        scrollDirection: Axis.horizontal,
        itemBuilder: (context, index) {
          return _ListViewItme(index);
        },
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Container(
      height: ScreenUtil().setHeight(400),
      margin: EdgeInsets.only(top: 10),
      child: Column(
        children: [_titleItem(), _recommendList()],
      ),
    );
  }
}

注意:

  • 这里我们把一个复杂的组件,分成三个小组件
  • decoration:设置边框

6.3.7、主页火爆商品—流体布局+上拉刷新------>flutter_easyrefresh

  • home_page.dart—>布局
//  火爆商品的流水布局与网格布局差不多
  Widget _wrapList() {
    if (this.hotGoods.length != 0) {
//    使用集合装子组件
      List listWidget = hotGoods.map((val) {
        return InkWell(
          onTap: () {},
          child: Container(
            width: ScreenUtil().setWidth(372),
            color: Colors.white,
            padding: EdgeInsets.all(5),
            margin: EdgeInsets.only(bottom: 3),
            child: Column(
              children: [
                Image.network(
                  val['img'],
                  width: ScreenUtil().setWidth(370),
                ),
                Text(
                  val["name"],
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  style: TextStyle(
                      color: Colors.pink, fontSize: ScreenUtil().setSp(26)),
                ),
                Row(
                  children: [
                    Text('¥${val['money']}'),
                    Text(
                      '¥${val['newMoney']}',
                      style: TextStyle(
                          color: Colors.grey,
                          decoration: TextDecoration.lineThrough),
                    ),
                  ],
                )
              ],
            ),
          ),
        );
      }).toList();
//  流水布局
      return Wrap(
//    两列
        spacing: 2,
        children: listWidget,
      );
    } else {
      return Text("没有数据...");
    }
  }

注意:

  • 这里使用了流体布局,其实和网格布局一样。

  • 首先把组件装入一个List中

  • 然后返回一个Wrap流体布局

    • spacing:2列

    • children:字布局集合

  • home_page.dart—>上拉刷新**
//              自定义上拉刷新底部
              footer: ClassicalFooter(
//                背景颜色
                bgColor: Colors.white,
//                字体颜色
                textColor: Colors.pink,
//                加载时颜色
              infoColor: Colors.pink,
//              加载时的主标题
              loadedText: "数据正在过来...",
//                到底的字体
              noMoreText: '到底了',
//                加载时的文字
                infoText: "正在准备数据中...",
                loadReadyText: "上拉加载"

              ),
              child: ListView(
                children: [
                  SwiperWidget(
                    swiperDataList: listData,
                  ),
                  TopNavigator(
                    navigatorList: navgatorList,
                  ),
                  AdBanner(
                    imgUrl:
                        "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3708973878,1447368046&fm=26&gp=0.jpg",
                  ),
                  LeaderPhone(
                    imgUrl:
                        "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=146387912,579346698&fm=11&gp=0.jpg",
                    phone: "17674078238",
                  ),
                  Recommend(
                    recommendList: reommedList,
                  ),
                  FloorLayout(
                    floorImgs: floorImgs,
                  ),
                  FloorLayout(
                    floorImgs: floorImgs,
                  ),
                  _hotGoods()
                ],
              ),
              onLoad: () async{
//                上拉刷新回调
                //    这里就不访问网络了,之间假数据
                List newGoodsList = [
                  {
                    "img":
                    "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=146387912,579346698&fm=11&gp=0.jpg",
                    "name": "美酒"+this.page.toString(),
                    "money": "123213",
                    "newMoney": "432"
                  },
                  {
                    "img":
                    "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=146387912,579346698&fm=11&gp=0.jpg",
                    "name": "美酒"+(this.page+1).toString(),
                    "money": "123213",
                    "newMoney": "432"
                  },
                ];
                setState(() {
//      刷新页面
                  hotGoods.addAll(newGoodsList);
                  if(hotGoods.length>10){
                    hotGoods.clear();
                  }
                  this.page++;
                  this.page++;
                });
              },
            );

注意:

  • 这里修改了以前的布局
  • 外父容器中:EasyRefresh
  • 里嵌套中使用ListView
  • 使用footer来自定义
    • ClassicalFooter里包括了值
    • onLoad刷新的回调

6.4、分类开发

6.4.1、分类页左侧主导航----provide(状态管理也就是全局双向数据绑定)

  • provide插件

这个是谷歌自己出来的一个插件,用于管理状态的。

可以理解为是一个双向数据绑定,而且是全局的。

双向数据绑定就是,数据改变页面也会跟着改变。

  • pubspec.yml
dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  dio: ^3.0.0
  flutter_swiper: ^1.1.4
  flutter_screenutil: ^1.0.2
  url_launcher: ^5.1.3
  flutter_easyrefresh: ^2.1.1
  provide: ^1.0.2

导入provide

  • 创建一个counter.dart全局类
import 'package:flutter/material.dart';


//存入一个全局双向数据
class Counter with ChangeNotifier{
  int value = 0;

  increment(index){
    value = index;
//    刷新数据
    notifyListeners();
  }
}

创建一个全局类,并准备一个修改属性的方法,方法得刷新。

  • main.dart
//导入插件并添加依赖
import 'package:flutter/material.dart';
//ios样式
import 'package:flutter/cupertino.dart';
import 'package:flutter_electricity/pages/index_page.dart';
//导入内容通过这provide双向数据绑定
import 'package:provide/provide.dart';
import 'provide/counter.dart';//导入双向绑定数据里的数据类


void main() {
//  绑定全局双向数据模板
  var counter = Counter();
  var providers = Providers();
  providers..provide(Provider.value(counter));
  runApp(ProviderNode(child: MyApp(),providers: providers,));
}

在lib—>创建provide–>counter.dart

修改了mian方法,这是官网的一个模板,不好解释,具体看文档;

  • category_page.dart
//适合Android的布局
import 'package:flutter/material.dart';
import 'package:flutter_electricity/servceFun/servce_method.dart';
import 'dart:convert'; //引入json解析插件
import 'package:flutter_screenutil/flutter_screenutil.dart'; //导入适配组件
//导入内容通过这provide双向数据绑定
import 'package:provide/provide.dart';
import '../provide/counter.dart';//导入双向绑定数据里的数据类

class CateGoryPage extends StatefulWidget {
  @override
  _CateGoryPage createState() => _CateGoryPage();
}

class _CateGoryPage extends State {
  @override
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Scaffold(
        appBar: AppBar(title: Text("分类"),),
        body: Container(
          child: Row(
            children: [
              LeftCatgegoryNav(),
              Provide (
                builder: (context,child,counter){
                  return Text("${counter.value}");
                },
              )
            ],
          ),
        ),
      ),
    );
  }
}
//左侧主导航
class LeftCatgegoryNav extends StatefulWidget {

  @override
  _LeftCatgegoryNavState createState() => _LeftCatgegoryNavState();
}

class _LeftCatgegoryNavState extends State {
  List list = [];
  @override
  void initState() {
    _getCategory();
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Container(
      width: ScreenUtil().setWidth(180),
      decoration: BoxDecoration(
        border: Border(
          right: BorderSide(color: Colors.grey,width: 1)
        )
      ),
      child: ListView.builder(
        itemCount: this.list.length,
        itemBuilder: (context,index){
         return _leftInkWell(index);
        },
      ),
    );
  }
//  子组件
  Widget _leftInkWell(int index){
    return InkWell(
      onTap: (){
         Provide.value(context).increment(index);
      },
      child: Container(
        height: ScreenUtil().setHeight(100),
        padding: EdgeInsets.only(left: 10,top: 20),
        decoration: BoxDecoration(
          color: Colors.white,
          border: Border(
            bottom: BorderSide(width: 1,color: Colors.grey)
          )
        ),
        child: Text(this.list[index]["navMain"],style: TextStyle(color: Colors.pink,fontSize: ScreenUtil().setSp(26)),),
      ),
    );
  }
//  获取数据
  void _getCategory(){
    getHomePageData().then((val){
      //使用假数据
      List cateList = [{'navMain':"分类1"},{'navMain':"分类2"},{'navMain':"分类3"}];
      setState(() {
        this.list = cateList;
      });
    });
  }
}

注意:

  • 首先得导入provide
  • 使用全局数据
Provide (
    builder: (context,child,counter){
        return Text("${counter.value}");
    },
    
  • 调用方法改变数据

       Provide.value(context).increment(index);
    
  • 在其他也是用导入包,操作一样即可。

6.4.2、分类页右侧二级导航

这里使用provide来保持状态

  • counter.dar(状态保持类)
import 'package:flutter/material.dart';


//存入一个全局双向数据
class StateRightRiCategoryNav with ChangeNotifier{
  int index = 0;

  increment(index){
    this.index = index;
//    刷新数据
    notifyListeners();
  }
}

因为假数据的原因,这里我只保存了一个索引值

  • 修改main.dart注入provide依赖
void main() {
//  绑定全局双向数据模板
  var state_RightRiCategoryNav = StateRightRiCategoryNav();
  var providers = Providers();
  providers..provide(Provider.value(state_RightRiCategoryNav));
  runApp(ProviderNode(child: MyApp(),providers: providers,));
}

这里按模板注入即可

  • category_page.dart二级导航
//右侧二级导航
class RightCategoryNav extends StatefulWidget {
  @override
  _RightCategoryNavState createState() => _RightCategoryNavState();
}

class _RightCategoryNavState extends State {
  List list = [
    ['名酒1', '宝丰1', '北京二锅头1', '大明1', '散酒1', '红酒1', '白酒1'],
    ['名酒2', '宝丰2', '北京二锅头2', '大明2', '散酒2', '红酒2', '白酒2'],
    ['名酒3', '宝丰3', '北京二锅头3', '大明3', '散酒3', '红酒3', '白酒3'],
  ];

  @override
  Widget build(BuildContext context) {
    return Container(
      height: ScreenUtil().setHeight(80),
      width: ScreenUtil().setWidth(570),
      decoration: BoxDecoration(
          border: Border(bottom: BorderSide(width: 1, color: Colors.grey))),
      child: ListView.builder(
        scrollDirection: Axis.horizontal,
        itemCount: list.length,
        itemBuilder: (context, index) {
//          使用全局状态管理数据
          return Provide(builder: (context,child,counter){
            return _rightInkWell(list[counter.index][index]);
          },);
//          return _rightInkWell(list[index]);
        },
      ),
    );
  }

//  子组件
  Widget _rightInkWell(String item) {
    return InkWell(
      onTap: () {},
      child: Container(
        padding: EdgeInsets.fromLTRB(5, 10, 5, 10),
        child: Text(
          item,
          style: TextStyle(fontSize: ScreenUtil().setSp(28)),
        ),
      ),
    );
  }
//
}

注意:

  • list是一个四数据

  • 传入到子组件中的list是用provide状态管理来传入的

6.4.3、分类页右侧内容数据–>fluttertoast吐丝插件

  • category_page
//适合Android的布局
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:flutter_electricity/servceFun/servce_method.dart';
import 'dart:convert'; //引入json解析插件
import 'package:flutter_screenutil/flutter_screenutil.dart'; //导入适配组件
//导入内容通过这provide双向数据绑定
import 'package:provide/provide.dart';
import '../provide/counter.dart'; //导入双向绑定数据里的数据类

//  定义一个跳转listView列表
var scorllController = new ScrollController();

class CateGoryPage extends StatefulWidget {
  @override
  _CateGoryPage createState() => _CateGoryPage();
}

class _CateGoryPage extends State {
  @override
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Scaffold(
        appBar: AppBar(
          title: Text("分类"),
        ),
        body: Container(
          child: Row(
            children: [
              LeftCatgegoryNav(),
              Column(
                children: [RightCategoryNav(), CategorGoods()],
              )
            ],
          ),
        ),
      ),
    );
  }
}

//左侧主导航
class LeftCatgegoryNav extends StatefulWidget {
  @override
  _LeftCatgegoryNavState createState() => _LeftCatgegoryNavState();
}

class _LeftCatgegoryNavState extends State {
  List list = [];
  int listIndex = 0;

  @override
  void initState() {
    _getCategory();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: ScreenUtil().setWidth(180),
      decoration: BoxDecoration(
          border: Border(right: BorderSide(color: Colors.grey, width: 1))),
      child: ListView.builder(
        itemCount: this.list.length,
        itemBuilder: (context, index) {
          return _leftInkWell(index);
        },
      ),
    );
  }

//  子组件
  Widget _leftInkWell(int index) {
    bool isClickList = false;
    isClickList = (index == listIndex) ? true : false;
    return InkWell(
      onTap: () {
        setState(() {
          listIndex = index;
        });
        Provide.value(context).increment(index);
      },
      child: Container(
        height: ScreenUtil().setHeight(100),
        padding: EdgeInsets.only(left: 10, top: 20),
        decoration: BoxDecoration(
            color: isClickList ? Colors.black26 : Colors.white,
            border: Border(bottom: BorderSide(width: 1, color: Colors.grey))),
        child: Text(
          this.list[index]["navMain"],
          style:
              TextStyle(color: Colors.pink, fontSize: ScreenUtil().setSp(26)),
        ),
      ),
    );
  }

//  获取数据
  void _getCategory() {
    //使用假数据
    List cateList = [
      {'navMain': "分类1"},
      {'navMain': "分类2"},
      {'navMain': "分类3"}
    ];
    setState(() {
      this.list = cateList;
    });
//    getHomePageData().then((val){
//
//    });
  }
}

//右侧二级导航
class RightCategoryNav extends StatefulWidget {
  @override
  _RightCategoryNavState createState() => _RightCategoryNavState();
}

class _RightCategoryNavState extends State {
  int itemIndex = 0; //点击的子导航
  List list = [
    ['全部', '名酒1', '宝丰1', '北京二锅头1', '大明1', '散酒1', '红酒1', '白酒1'],
    ['全部', '名酒2', '宝丰2', '北京二锅头2', '大明2', '散酒2', '红酒2', '白酒2'],
    ['全部', '名酒3', '宝丰3', '北京二锅头3', '大明3', '散酒3', '红酒3', '白酒3'],
  ];

  @override
  Widget build(BuildContext context) {
    return Container(
      height: ScreenUtil().setHeight(80),
      width: ScreenUtil().setWidth(570),
      decoration: BoxDecoration(
          border: Border(bottom: BorderSide(width: 1, color: Colors.grey))),
      child: Provide(
        builder: (context, child, counter) {
          return ListView.builder(
            scrollDirection: Axis.horizontal,
            itemCount: this.list[counter.index].length,
            itemBuilder: (context, index) {
//          使用全局状态管理数据
              return _rightInkWell(list[counter.index][index], index);
//          return _rightInkWell(list[index]);
            },
          );
        },
      ),
    );
  }

//  子组件
  Widget _rightInkWell(String item, index) {
    bool itemIs = itemIndex == index ? true : false;
    return InkWell(
      onTap: () {
        scorllController.jumpTo(0);
        setState(() {
          itemIndex = index;
        });
      },
      child: Container(
        padding: EdgeInsets.fromLTRB(5, 10, 5, 10),
        child: Text(
          item,
          style: TextStyle(
              fontSize: ScreenUtil().setSp(28),
              color: itemIs ? Colors.pink : Colors.black),
        ),
      ),
    );
  }
//
}

//右侧主内容区
class CategorGoods extends StatefulWidget {
  @override
  _CategorGoodsState createState() => _CategorGoodsState();
}

class _CategorGoodsState extends State {

  //设置假数据
  List goodsList = [
    {
      'img':
          "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=146387912,579346698&fm=11&gp=0.jpg",
      "name": "北京二锅头 55度1",
      "newMoney": "134",
      "money": "1423"
    },
    {
      'img':
          "https://img.alicdn.com/imgextra/i4/2201274259957/O1CN01vGHsvY2NQMCIR3qfB_!!2201274259957.jpg_430x430q90.jpg",
      "name": "白酒 123度1",
      "newMoney": "43",
      "money": "1643"
    },
    {
      'img':
          "https://img.alicdn.com/imgextra/i4/3395417731/O1CN01lW1Wrk26yqaCL9Tk2-3395417731.jpg_430x430q90.jpg",
      "name": "红酒 22度1",
      "newMoney": "44",
      "money": "5435"
    },
    {
      'img':
          "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=146387912,579346698&fm=11&gp=0.jpg",
      "name": "北京二锅头 55度2",
      "newMoney": "134",
      "money": "1423"
    },
    {
      'img':
          "https://img.alicdn.com/imgextra/i4/2201274259957/O1CN01vGHsvY2NQMCIR3qfB_!!2201274259957.jpg_430x430q90.jpg",
      "name": "白酒 123度2",
      "newMoney": "43",
      "money": "1643"
    },
    {
      'img':
          "https://img.alicdn.com/imgextra/i4/3395417731/O1CN01lW1Wrk26yqaCL9Tk2-3395417731.jpg_430x430q90.jpg",
      "name": "红酒 22度2",
      "newMoney": "44",
      "money": "5435"
    },
  ];

  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Container(
          width: ScreenUtil().setWidth(570),
//          使用了Expanded伸缩父容器,就无需加高
//          height: ScreenUtil().setHeight(950),
          child: EasyRefresh(
//          自定义上拉刷新底部
            footer: ClassicalFooter(
//                背景颜色
                bgColor: Colors.white,
//                字体颜色
                textColor: Colors.pink,
//                加载时颜色
                infoColor: Colors.pink,
//              加载时的主标题
                loadedText: "数据正在过来...",
//                到底的字体
                noMoreText: '到底了',
//                加载时的文字
                infoText: "正在准备数据中...",
                loadReadyText: "上拉加载"),
            child: ListView.builder(
                controller: scorllController,
                itemCount: this.goodsList.length,
                itemBuilder: (context, index) {
                  return _listViewItem(this.goodsList[index], index);
                }),
            onLoad: () async {
//                模拟添加数据
              setState(() {
                Map addObj = {
                  'img':
                      "https://img.alicdn.com/imgextra/i4/3395417731/O1CN01lW1Wrk26yqaCL9Tk2-3395417731.jpg_430x430q90.jpg",
                  "name": "红酒 22度2",
                  "newMoney": "44",
                  "money": "5435"
                };
                goodsList.add(addObj);
                if (goodsList.length > 12) {
                              //提示到底了
                  Fluttertoast.showToast(
                      msg: "到底了",
                      //提示时间
                      toastLength: Toast.LENGTH_LONG,
                      //提示位置
                      gravity: ToastGravity.BOTTOM,
                      backgroundColor: Colors.pink,
                      textColor: Colors.white,
                      fontSize: ScreenUtil().setSp(16));
                }
              });
            },
          )),
    );
  }

//  子组件--->listItem
  Widget _listViewItem(Map item, int index) {
    return InkWell(
      onTap: () {},
      child: Container(
        decoration: BoxDecoration(
            border: Border(bottom: BorderSide(width: 1, color: Colors.grey))),
        padding: EdgeInsets.all(10),
        width: ScreenUtil().setWidth(570),
        height: ScreenUtil().setHeight(200),
        child: Row(
          children: [
            Image.network(
              item['img'],
              width: ScreenUtil().setWidth(200),
            ),
            Container(
              margin: EdgeInsets.only(left: 20),
              child: Column(
                children: [
                  Container(
                    child: Text(
                      "${item['name']}",
                      style: TextStyle(
                        fontSize: ScreenUtil().setSp(36),
                      ),
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                    ),
                    width: ScreenUtil().setWidth(210),
                  ),
                  Container(
                    width: ScreenUtil().setWidth(210),
                    margin: EdgeInsets.only(top: 10),
                    child: Row(
                      children: [
                        Text(
                          "价格:¥${item['newMoney']}",
                          style: TextStyle(
                              fontSize: ScreenUtil().setSp(26),
                              color: Colors.pink),
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis,
                        ),
                        Text(
                          "¥${item['money']}",
                          style: TextStyle(
                              fontSize: ScreenUtil().setSp(16),
                              decoration: TextDecoration.lineThrough),
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis,
                        ),
                      ],
                    ),
                  )
                ],
              ),
            )
          ],
        ),
      ),
    );
  }
}

注意:这里是分类的全部代码

  • ScrollController()是一个listview的跳转类
    • 首先定义全局的:var scorllController = new ScrollController();
    • 其次在跳转的listview中调节controller: scorllController,属性
    • 最后是跳转代码:scorllController.jumpTo(0);这里是跳转到最前面;
  • Expanded父容器布局是一个拉伸布局
    • 它可以不用设置高.
  • Fluttertoast用于吐丝的一个插件

6.5、详细数据页面开发

6.5.1、详细页面开发---->fluro路由插件

  • 首先完成fluro框架

    • 导入插件import ‘package:fluro/fluro.dart’;需要在pubspec.yml文件加入版本。

    • 创建一个routers文件夹

      • 创建routers.dart主配置文件
      import 'package:fluro/fluro.dart';
      import 'package:flutter/material.dart';
      import 'package:flutter_electricity/routers/router_handler.dart';
      
      class Routers{
      //  配置路由路径
        static String root='/';
        static String detailspage ='/detail';
        static void configureRouter(Router router){
          //找不到路由
          handlerFunc: (BuildContext context,Map> pagems){
            print("找不到页面");
          };
          router.define(detailspage, handler: detailsHandler);  //找到路由并跳转
        }
      }
      

      注意:

      • 配置的detail路径是用来访问页面的
      • detailsHandler是跳转的下一个页面,这里再router_handler包下
      • 创建一个router_handler.dart跳转配置文件
      import 'package:flutter/material.dart';
      import 'package:fluro/fluro.dart';
      import '../pages/details_page.dart';
      
      
      Handler detailsHandler = Handler(
        handlerFunc: (BuildContext context,Map> pagems){
          String goodsId = pagems['id'].first;
          print("页面id:${goodsId}");
          return DetailsPage(goodsId);
        }
      );
      

      注意:

      • 这里是配置一个跳转的handler路由文件
      • pagems用来接收跳转时传入的一个参数
      • 返回DetailsPage页面并把参数给页面
      • ,Map pagems是模板
      • routerApplication.dart静态化router使得使用时,不需要实例化
      import 'package:fluro/fluro.dart';
      
      //静态化router
      class routerApp{
        static Router router;
      }
      

      这个可写可不写,建议写好。

    • 在main.dart文件注入router依赖,并把路由显示到容器

    class MyApp extends StatelessWidget{
      @override
      Widget build(BuildContext context) {
    //    注入路由显示
      final router = Router();
      Routers.configureRouter(router);//添加路由配置
      routerApp.router = router;  //路由静态化
        // TODO: implement build
        return MaterialApp(
          title: "电商管理",
          onGenerateRoute: routerApp.router.generator,  //把路由来的页面放在容器中显示
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            primaryColor: Colors.pink
          ),
          home: Scaffold(
    //        appBar: new AppBar(title: Text("主页"),),
            body: Center(
              child: IndexPage(),
            ),
          ),
        );
      }
    
    }
    

    注意:

    • 在build方法内,注入router配置
    • onGenerateRoute用来指定路由显示在那个容器中。
      • routerApp.router.generator路由页面
  • 在相应位置中,点击router跳转

//            一路由跳转到详细页面
routerApp.router.navigateTo(context, "/detail?id=${Random().nextInt(10)}");

注意:

  • 参数1-上下文
  • 参数2-路径+参数

6.5.2、详细数据页,使用Provdio来获取网络数据

  • 在provide文件夹里新建一个details_info.dart文件
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_electricity/model/details.dart';
import 'dart:async';
import '../servceFun/servce_method.dart';


//注册商品详细的全局数据
class DateilsInfoProvide with ChangeNotifier{
  DetailsEntity detailsData = null;
   getGoodsInfo(){
     getHomePageData().then((val){
//       转为实体
       var details = json.decode(val.toString());
       detailsData = DetailsEntity.fromJson(details);
       //刷新
       notifyListeners();
     });
   }
}

注意:

  • 这里使用全局绑定数据在detailsData
  • 这里使用接收json转为实体对象,然后刷新
  • 在main.dart中注入
void main() {
//  绑定全局双向数据模板
  var state_RightRiCategoryNav = StateRightRiCategoryNav();	//绑定Provide
  var  dateilsInfoProvide= DateilsInfoProvide();
  var providers = Providers();
  providers..provide(Provider.value(state_RightRiCategoryNav));	//载入Provie
  providers..provide(Provider.value(dateilsInfoProvide));
  runApp(ProviderNode(child: MyApp(),providers: providers,));
}
  • providers…provide方式注入即可
  • 这是就可以开始使用了

6.5.3、详细页面开发,ui基本布局----flutter_html显示网页

  • details_page.dart
return Container(
    child: ListView(
        children: [
            DetailsTopArea(), //这是详细页面--头部
            DetailsExplain(),  //详细页面--通知
            DetailsTabbar(),  //切换标签
            DetailsWeb()    //显示切换页面
        ],
    ),
);

这里主要添加子页面

  • 在details_page文件夹里创建一个details_tabbar.dart
import 'package:flutter/material.dart';
import 'package:flutter_electricity/provide/details_info.dart';
import 'package:flutter_screenutil/screenutil.dart';
import 'package:provide/provide.dart';

class DetailsTabbar extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provide(builder: (context, chuild, data) {
      return Container(
        color: Colors.white,
        margin: EdgeInsets.only(top: 10, left: 15),
        width: ScreenUtil().setWidth(750),
        child: Row(
          children: [
            InkWell(
              onTap: () {
                Provide.value(context).updateIsMsg(1);
              },
              child: Container(
                decoration: BoxDecoration(
                    border: Border(
                        bottom: data.isMsg1
                            ? BorderSide(width: 2, color: Colors.pink)
                            : BorderSide(width: 0))),
                alignment: Alignment.center,
                width: ScreenUtil().setWidth(350),
                child: Text(
                  "详细",
                  style: TextStyle(fontSize: ScreenUtil().setSp(30)),
                ),
              ),
            ),
            InkWell(
              onTap: () {
                Provide.value(context).updateIsMsg(2);
              },
              child: Container(
                decoration: BoxDecoration(
                    border: Border(
                        bottom: data.isMsg2
                            ? BorderSide(width: 2, color: Colors.pink)
                            : BorderSide(width: 0))),
                alignment: Alignment.center,
                width: ScreenUtil().setWidth(350),
                child: Text(
                  "商品",
                  style: TextStyle(fontSize: ScreenUtil().setSp(30)),
                ),
              ),
            )
          ],
        ),
      );
    });
  }
}

这个没什么技术难点,就两个便签利用provide值切换样式

  • 同样在文件夹里创建dateils_web.dart
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:provide/provide.dart';
import 'package:flutter_electricity/provide/details_info.dart';

class DetailsWeb extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provide(builder: (context,chuild,data){
      int indexMsg = data.isMsg1?1:2;
      return Container(
        child: Html(
          data: data.detailsData["html"+indexMsg.toString()],
        ),
      );
    });
  }
}

注意:

  • 这里使用了flutter_html插件,用来显示html网页
    • 导入flutter_html: any,我当前用的版本是1.0.0但与其他依赖冲突,所以使用any适配依赖注入
    • 使用Provide里的值显示即可
  • details_info.dat
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_electricity/model/details.dart';
import 'dart:async';
import '../servceFun/servce_method.dart';

//注册商品详细的全局数据
class DateilsInfoProvide with ChangeNotifier {
    Map detailsData = null;
    bool isMsg1 = true;
    bool isMsg2 = false;

    getGoodsInfo()  async{
        getHomePageData().then((val) {
            //       转为实体
            var details = json.decode(val.toString());
            //       detailsData = DetailsEntity.fromJson(details);
            detailsData = {
                "url":
                "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2809691443,3777995480&fm=26&gp=0.jpg",
                "name": "红酒",
                "num": "01110321",
                "newMoney": "288",
                "money": "490",
                "html1":'自己网上找',
                "html2":'自己网上找'
            };
            //       这里就模拟数据,不用接口数据

            //刷新
            notifyListeners();
        });
    }

    //切换详细信息
    updateIsMsg(int msg) {
        if(msg == 1){
            this.isMsg1 = true;
            this.isMsg2 = false;
        }else{
            this.isMsg1 = false;
            this.isMsg2 = true;
        }
        notifyListeners();
    }
}

注意:

  • 这里用的都是静态的,模拟数据。但调用了接口

6.5.4、详细页面开发-底部购买区

  • 修改details_page页面的父布局为重叠布局

    return Stack(
        children: [
            //                内容详细
            Container(
                child: ListView(
                    children: [
                        DetailsTopArea(), //这是详细页面--头部
                        DetailsExplain(), //详细页面--通知
                        DetailsTabbar(), //切换标签
                        DetailsWeb() //显示切换页面
                    ],
                ),
            ),
            //                底部购买区域
            Positioned(
                bottom: 0,
                left: 0,
                child: DetailsBottom(),
            )
        ],
    );
    

    这里使用使用重叠布局,吧购买区域悬浮最下面

  • 在details_page文件夹里创建details_bottom.dart

import 'package:flutter/material.dart';
import 'package:flutter_electricity/provide/details_info.dart';
import 'package:flutter_screenutil/screenutil.dart';
import 'package:provide/provide.dart';

class DetailsBottom extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      width: ScreenUtil().setWidth(750),
      color: Colors.white,
      height: ScreenUtil().setHeight(100),
      child: Row(
        children: [
//          图标
          InkWell(
            onTap: (){},
            child: Container(
              width: ScreenUtil().setWidth(110),
              alignment: Alignment.center,
              child: Icon(Icons.shopping_cart,size: 35,color: Colors.pink,),
            ),
          ),
          InkWell(
            onTap: (){},
            child: _BottomAdd(),
          ),
          InkWell(
            onTap: (){},
            child: _BottomMai(),
          )
        ],
      ),
    );
  }
//  加入购物车
Widget _BottomAdd(){
    return Container(
      alignment: Alignment.center,
      width: ScreenUtil().setWidth(320),
      height: ScreenUtil().setHeight(100),
      color: Colors.pink,
      child: Text("加入购物车",style: TextStyle(
        color: Colors.white,
        fontSize: ScreenUtil().setSp(28)
      ),),
    );
}
//  购买
  Widget _BottomMai(){
    return Container(
      alignment: Alignment.center,
      width: ScreenUtil().setWidth(320),
      height: ScreenUtil().setHeight(100),
      color: Colors.amberAccent,
      child: Text("立即购买",style: TextStyle(
          color: Colors.white,
          fontSize: ScreenUtil().setSp(28)
      ),),
    );
  }
}

这里使用InkWell布局才能点击

6.6、购物车页面开发

6.6.1、使用shared_preferences来存入本地数据–测试------shared_preferences

  • 首先导入插件,并在pubspec.yml导入版本
  • 在provide文件夹里创建一个cart_info.dat文件
import 'dart:convert';

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

class CartProvide with ChangeNotifier {
  String cartString = null;

//  保存方法
  save(goodsName, money, image) async {
    SharedPreferences prefs = await SharedPreferences.getInstance(); //实例化本地储存
    cartString = prefs.getString("cartInfo"); //获取本地值
    var temp = cartString == null
        ? []
        : json.decode(cartString.toString()); //将字符串转为list
    List tempList = (temp as List).cast(); //转为list
    //判断重复并count++
    bool isSave = false;
    int ival = 0;
    tempList.forEach((item) {
      //循环
      if (item['goodsName'] == goodsName) {
        //如果找到一样的商品
        tempList[ival]['count'] = item['count'] + 1;
        isSave = true; //标记已经添加
      }
      ival++;
    });
//    真正的添加
    if (!isSave) {
      tempList.add({
        'goodsName': goodsName,
        'count': 1,   //第一次添加为0
        'money': money,
        'image': image,
      });
    }

//  将list转为字符串并存入本地
    cartString = json.encode(tempList).toString();
    prefs.setString("cartInfo", cartString);
    print(cartString);
    notifyListeners();  //刷新provide
  }

//  清空方法
  remove() async {
    SharedPreferences prefs = await SharedPreferences.getInstance(); //实例化本地储存
    prefs.remove("cartInfo");
    print("清空完成--------------");
    notifyListeners();//刷新provide
  }
}

注意:

  • 这里主要使用了SharedPreferences来储存数据
  • SharedPreferences储存式只能储存字符串
    • json.decode(cartString.toString()); //将字符串转为list
    • ​ List tempList = (temp as List).cast(); //转为list
    • json.encode(tempList).toString();//将list转为字符串
  • 准备了两个方法:save用于保存,remove用于清空,当然这里数据都是虚拟的。
  • ​ prefs.setString(“cartInfo”, cartString);存入即可.
  • main.dart中注入依赖

    void main() {
    //  绑定全局双向数据模板
      var state_RightRiCategoryNav = StateRightRiCategoryNav();
      var  dateilsInfoProvide= DateilsInfoProvide();
      var  cartInfoProvide= CartProvide();//---
      var providers = Providers();
      providers..provide(Provider.value(state_RightRiCategoryNav));
      providers..provide(Provider.value(dateilsInfoProvide));
      providers..provide(Provider.value(cartInfoProvide));//---
      runApp(ProviderNode(child: MyApp(),providers: providers,));
    }
    
  • 最后调用其方法

onTap: () {
    //              加入购物车
    Provide.value(context).save(goodsInfo['name'], goodsInfo['newMoney'], goodsInfo['url']);
},
onTap: (){
    //清空购物车
    Provide.value(context).remove();
},

6.6.2、购物车页面开发—公司规范性

  • cart_info.dart数据处理文件
import 'dart:convert';
import 'dart:math';

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

class CartProvide with ChangeNotifier {
  String cartString = null;
  List listCart = null;
  double moneyAll = 0;
  int goodsCount = 0;
  bool isAllCheack = false;

//  保存方法
  save(goodsName, money, image, isCheack) async {
    goodsName = goodsName + Random().nextInt(3).toString();
    SharedPreferences prefs = await SharedPreferences.getInstance(); //实例化本地储存
    cartString = prefs.getString("cartInfo"); //获取本地值
    var temp = cartString == null
        ? []
        : json.decode(cartString.toString()); //将字符串转为list
    List tempList = (temp as List).cast(); //转为list
    //判断重复并count++
    bool isSave = false;
    int ival = 0;
    goodsCount = 0;
    tempList.forEach((item) {
      //循环
      if (item['goodsName'] == goodsName) {
        //如果找到一样的商品
        tempList[ival]['count'] = item['count'] + 1;
        isSave = true; //标记已经添加
      }
      if (item["isCheack"]) {
        goodsCount++;
//        moneyAll+=;
      }
    });
//    真正的添加
    if (!isSave) {
      tempList.add({
        'goodsName': goodsName,
        'count': 1, //第一次添加为0
        'money': money,
        'image': image,
        'isCheack': isCheack
      });
    }

//  将list转为字符串并存入本地
    cartString = json.encode(tempList).toString();
    prefs.setString("cartInfo", cartString);
    notifyListeners(); //刷新provide
  }

//  清空方法
  remove() async {
    SharedPreferences prefs = await SharedPreferences.getInstance(); //实例化本地储存
    prefs.remove("cartInfo");
    print("清空完成--------------");
    notifyListeners(); //刷新provide
  }

//  删除一个购物车商品
  deleteOneGoods(String goodsName) async {
    SharedPreferences prefs = await SharedPreferences.getInstance(); //实例化本地储存
    cartString = prefs.getString('cartInfo');
    List tempList = (json.decode(cartString.toString()) as List).cast();

    for (int i = 0; i < tempList.length; i++) {
      if (tempList[i]['goodsName'] == goodsName) {
        tempList.removeAt(i);
        break;
      }
    }
    cartString = json.encode(tempList).toString();
    prefs.setString("cartInfo", cartString);
    await getCartData();
  }

  //查询
  getCartData() async {
    SharedPreferences prefs = await SharedPreferences.getInstance(); //实例化本地储存
    cartString = prefs.getString("cartInfo");
    var temp = cartString == null
        ? []
        : json.decode(cartString.toString()); //将字符串转为list
    listCart = (temp as List).cast(); //转为list
    moneyAll = 0;
    goodsCount = 0;
    double temMomey = 0;
    for (int i = 0; i < listCart.length; i++) {
      if (listCart[i]['isCheack']) {
        goodsCount++;
        temMomey = double.parse(listCart[i]['money']);
      }
    }
    moneyAll += temMomey;
    print(listCart.length);
    if (goodsCount == listCart.length)
      isAllCheack = true;
    else
      isAllCheack = false;
    notifyListeners();
  }

//  选择单个
  changeCheckState(goodsName) async {
    SharedPreferences prefs = await SharedPreferences.getInstance(); //实例化本地储存
    cartString = prefs.getString("cartInfo");
    List tempList =
        (json.decode(cartString.toString()) as List).cast(); //转为list
    for (int i = 0; i < tempList.length; i++) {
      if (tempList[i]['goodsName'] == goodsName) {
        tempList[i]['isCheack'] = !tempList[i]['isCheack'];
        break;
      }
    }
    cartString = json.encode(tempList).toString();
    prefs.setString("cartInfo", cartString);
    await getCartData();
  }

//  全选
  changeCheckALl(bool val) async {
    SharedPreferences prefs = await SharedPreferences.getInstance(); //实例化本地储存
    cartString = prefs.getString("cartInfo");
    List tempList =
        (json.decode(cartString.toString()) as List).cast(); //转为list
    for (int i = 0; i < tempList.length; i++) {
      tempList[i]['isCheack'] = val;
    }
    cartString = json.encode(tempList).toString();
    prefs.setString("cartInfo", cartString);
    await getCartData();
  }

//  商品加减
  addOrReduceData(goodsName, bool isAR) async {
    SharedPreferences prefs = await SharedPreferences.getInstance(); //实例化本地储存
    cartString = prefs.getString("cartInfo");
    List tempList =
        (json.decode(cartString.toString()) as List).cast(); //转为list
    for (int i = 0; i < tempList.length; i++) {
      if (tempList[i]['goodsName'] == goodsName) {
        if (isAR) {
          //true为加
          if (tempList[i]['count'] < 10) tempList[i]['count']++;
        } else {
          //同理减
          tempList[i]['count']--;
          if (tempList[i]['count'] == 0) {
            deleteOneGoods(goodsName);
          }
        }
        break;
      }
    }
    cartString = json.encode(tempList).toString();
    prefs.setString("cartInfo", cartString);
    await getCartData();
  }
}

注意:

  • 这个主要处理数据,然后通过prefs保存,最后provide显示
  • cart_page.dart主界面
//适合Android的布局
import 'package:flutter/material.dart';
import 'package:flutter_electricity/provide/cart_info.dart';

//导入内容通过这provide双向数据绑定
import 'package:provide/provide.dart';
import '../provide/counter.dart';
import 'cart_page/cart_bottom.dart';
import 'cart_page/cart_item.dart'; //导入双向绑定数据里的数据类

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

class CartPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text("购物车"),
      ),
      body: FutureBuilder(
        future: _getCatData(context),
        builder: (context, data) {
          if (data.hasData) {
            return Stack(
              children: [
                Provide(builder: (context,chuild,data){
                  List list = Provide.value(context).listCart;
                  print("---");
                  print(list.toString()+"---");
                  return ListView.builder(
                    itemBuilder: (context, index) {
                      return CartItem(list[index]);
                    },
                    itemCount: list.length,
                  );
                }),
                Positioned(
                  bottom: 0,
                  left: 0,
                  child: CartBottom(),
                )
              ],
            );
          } else {
            return Text("正在加载...");
          }
        },
      ),
    );
  }

  Future _getCatData(BuildContext context) async {
    await Provide.value(context).getCartData();
    return "调用获取购物车";
  }
}

注意:

  • 这里我们把一个页面拆分了好几个组件显示
  • 子组件

    • cart_bottom.dart
    import 'package:flutter/material.dart';
    import 'package:flutter_electricity/provide/cart_info.dart';
    import 'package:flutter_screenutil/flutter_screenutil.dart';
    import 'package:provide/provide.dart';
    
    class CartBottom extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Container(
          padding: EdgeInsets.all(5.0),
          color: Colors.white,
          child:Provide(builder: (context,chuil,data){
            return  Row(
              children: [
                _selectAllBtn(context),
                _allPriceArea(data.moneyAll),
                _goButton(data.goodsCount)
              ],
            );
          },)
        );
      }
    
    //  子组件
      Widget _selectAllBtn(BuildContext context) {
        return Container(
          child: Provide(builder: (context,chuild,data){
            return Row(
              children: [
                Checkbox(
                  value: Provide.value(context).isAllCheack,
                  activeColor: Colors.pink,
                  onChanged: (bool val) {
                    Provide.value(context).changeCheckALl(val);
                  },
                ),
                Text("全选")
              ],
            );
          },)
        );
      }
    
    //中价格
      Widget _allPriceArea(double moneyAll) {
        return Container(
          width: ScreenUtil().setWidth(430),
          child: Column(
            children: [
              Row(
                children: [
                  Container(
                    alignment: Alignment.centerRight,
                    width: ScreenUtil().setWidth(280),
                    child: Text(
                      "合计",
                      style: TextStyle(fontSize: ScreenUtil().setSp(36)),
                    ),
                  ),
                  Container(
                    alignment: Alignment.centerLeft,
                    width: ScreenUtil().setWidth(150),
                    child: Text(
                      "¥${moneyAll}",
                      style: TextStyle(
                          fontSize: ScreenUtil().setSp(36), color: Colors.pink),
                    ),
                  ),
                ],
              ),
              Container(
                width: ScreenUtil().setWidth(430),
                alignment: Alignment.centerRight,
                child: Text(
                  "满10元免费配送,预购免配送费",
                  style: TextStyle(
                      color: Colors.black26, fontSize: ScreenUtil().setSp(22)),
                ),
              )
            ],
          ),
        );
      }
    //  确认订单
    Widget _goButton(int goodsCount){
        return Container(
          width: ScreenUtil().setWidth(160),
          padding: EdgeInsets.only(left: 10),
          child: InkWell(
            onTap: (){},
            child: Container(
              padding: EdgeInsets.all(10),
              alignment: Alignment.center,
              decoration: BoxDecoration(
                color: Colors.pink,
                borderRadius: BorderRadius.circular(3)
              ),
              child: Text("结算(${goodsCount})",style: TextStyle(
                color: Colors.white
              ),),
            ),
          ),
        );
    }
    }
    
    
    • cart_item.dart
    import 'package:flutter/material.dart';
    import 'package:flutter_electricity/provide/cart_info.dart';
    import 'package:flutter_screenutil/flutter_screenutil.dart';
    import 'package:provide/provide.dart';
    
    class CartItem extends StatelessWidget {
    //  准备一个数据
      final Map item;
    
      CartItem( this.item) ;
      @override
      Widget build(BuildContext context) {
        return Container(
          height: ScreenUtil().setHeight(180),
          margin: EdgeInsets.all(5),
          padding: EdgeInsets.all(5),
          decoration: BoxDecoration(
              color: Colors.white,
              border: Border(bottom: BorderSide(width: 1, color: Colors.black26))),
          child: Row(
            children: [
              _cartCheckBt(context,item['goodsName']),
              _cartImage(),
              _cartGoodsName(context,item),
              _cartPrice(context,item['goodsName'])
            ],
          ),
        );
      }
    
    //选择
      Widget _cartCheckBt(BuildContext context,goodsName) {
        return Container(
          child: Checkbox(
            value: item["isCheack"],
            activeColor: Colors.pink,
            onChanged: (bool val) {
              Provide.value(context).changeCheckState(goodsName);
            },
          ),
        );
      }
    
    //商品图片
      Widget _cartImage() {
        return Container(
          width: ScreenUtil().setWidth(150),
          padding: EdgeInsets.all(3),
          decoration:
              BoxDecoration(border: Border.all(width: 1, color: Colors.black26)),
          child: Image.network(item["image"]),
        );
      }
    
    //商品名称
      Widget _cartGoodsName(BuildContext context,item) {
        return Container(
          width: ScreenUtil().setWidth(300),
          padding: EdgeInsets.all(10),
          alignment: Alignment.topLeft,
          child: Column(
            children: [
              Text("${item["goodsName"]}",overflow: TextOverflow.ellipsis,),
              _cartCount(context,item)
            ],
          ),
        );
      }
    //  商品个数
      Widget _cartCount(BuildContext context,item){
        return Container(
            width: ScreenUtil().setWidth(165),
          margin: EdgeInsets.only(top: 10),
          decoration: BoxDecoration(
            border: Border.all(width: 1,color: Colors.black26)
          ),
          child: Provide(builder: (context,chuild,data){
            return Row(
              children: [
                InkWell(
                  onTap: (){
                    //减
                    Provide.value(context).addOrReduceData(item["goodsName"], false);
                  },
                  child: Container(
                    width: ScreenUtil().setWidth(45),
                    height: ScreenUtil().setHeight(45),
                    alignment: Alignment.center,
                    decoration: BoxDecoration(
                        color: Colors.white,
                        border: Border(right: BorderSide(width: 1,color: Colors.black26))
                    ),
                    child: Text("-"),
                  ),
                ),
                Container(
                  width: ScreenUtil().setWidth(70),
                  height:ScreenUtil().setHeight(45),
                  alignment: Alignment.center,
                  color: Colors.white,
                  child: Text("${item['count']}"),
                ),
                InkWell(
                  onTap: (){
                    //加
                    Provide.value(context).addOrReduceData(item["goodsName"], true);
                  },
                  child: Container(
                    width: ScreenUtil().setWidth(45),
                    height: ScreenUtil().setHeight(45),
                    alignment: Alignment.center,
                    decoration: BoxDecoration(
                        color: Colors.white,
                        border: Border(left: BorderSide(width: 1,color: Colors.black26))
                    ),
                    child: Text("+"),
                  ),
                )
              ],
            );
          },)
        );
      }
    //  价格
    Widget _cartPrice(BuildContext context,goodsName){
        return Container(
          width: ScreenUtil().setWidth(150),
          alignment: Alignment.centerRight,
          child: Column(
            children: [
              Text("¥${item['money']}"),
              Container(
                margin: EdgeInsets.only(top: 30),
                child: InkWell(
                  onTap: (){
                    Provide.value(context).deleteOneGoods(goodsName);
                  },
                  child: Icon(
                    Icons.delete_outline,
                    color: Colors.black26,
                    size: 30,
                  ),
                ),
              )
            ],
          ),
        );
    }
    }
    
    

6.7、会员中心页面开发

//适合Android的布局
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; //导入适配组件

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

class MembarPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text("会员中心"),
      ),
      body: Center(
          child: Container(
        child: ListView(
          children: [_topHeader(), _orderTitile(), _orderType(),_orderList()],
        ),
      )),
    );
  }

  _topHeader() {
    return Container(
      padding: EdgeInsets.only(top: 50),
      color: Colors.pink,
      child: Column(
        children: [
          Container(
              child: ClipOval(
            child: Image.network(
              "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=146387912,579346698&fm=11&gp=0.jpg",
              width: ScreenUtil().setWidth(200),
              height: ScreenUtil().setHeight(200),
            ),
          )),
          Container(
            child: Text(
              "会员中心",
              style: TextStyle(color: Colors.white),
            ),
          ),
        ],
      ),
    );
  }

  //订单标题
  _orderTitile() {
    return Container(
      margin: EdgeInsets.only(top: 10),
      decoration: BoxDecoration(
          color: Colors.white,
          border: Border(bottom: BorderSide(width: 1, color: Colors.black26))),
      child: ListTile(
        leading: Icon(Icons.list),
        title: Text("我的订单"),
        trailing: Icon(Icons.arrow_forward),
      ),
    );
  }

//  订单模块
  _orderType() {
    return Container(
      margin: EdgeInsets.only(top: 5),
      width: ScreenUtil().setWidth(750),
      height: ScreenUtil().setHeight(150),
      padding: EdgeInsets.only(top: 20),
      color: Colors.white,
      child: Row(
        children: [
          Container(
            width: ScreenUtil().setWidth(187),
            child: Column(
              children: [
                Icon(
                  Icons.query_builder,
                  size: 30,
                ),
                Text("待付款")
              ],
            ),
          ),
          //------------------
          Container(
            width: ScreenUtil().setWidth(187),
            child: Column(
              children: [
                Icon(
                  Icons.access_alarms,
                  size: 30,
                ),
                Text("待发货")
              ],
            ),
          ),
          //------------------
          Container(
            width: ScreenUtil().setWidth(187),
            child: Column(
              children: [
                Icon(
                  Icons.card_travel,
                  size: 30,
                ),
                Text("待收货")
              ],
            ),
          ),
          //------------------
          Container(
            width: ScreenUtil().setWidth(187),
            child: Column(
              children: [
                Icon(
                  Icons.filter_vintage,
                  size: 30,
                ),
                Text("待评价")
              ],
            ),
          ),
          //------------------
        ],
      ),
    );
  }

//  list
_orderList(){
    return Container(
      margin: EdgeInsets.only(top: 10),
      color: Colors.white,
      child: Column(
        children: [
          Container(
            padding: EdgeInsets.only(left: 10,right: 10),
            child: ListTile(
              leading: Icon(Icons.filter,size: 20,),
              title: Text("获取优惠卷"),
              trailing: Icon(Icons.arrow_forward,size: 20,),
            ),
          ),
          Container(
            padding: EdgeInsets.only(left: 10,right: 10),
            child: ListTile(
              leading: Icon(Icons.filter,size: 20,),
              title: Text("已领取优惠卷"),
              trailing: Icon(Icons.adjust,size: 20,),
            ),
          ),
          Container(
            padding: EdgeInsets.only(left: 10,right: 10),
            child: ListTile(
              leading: Icon(Icons.filter,size: 20,),
              title: Text("地址管理"),
              trailing: Icon(Icons.pin_drop,size: 20,),
            ),
          ),
          Container(
            padding: EdgeInsets.only(left: 10,right: 10),
            child: ListTile(
              leading: Icon(Icons.filter,size: 20,),
              title: Text("客户电话"),
              trailing: Icon(Icons.phone,size: 20,),
            ),
          ),
        ],
      )
    );
}
}

到这里结束了。

最后打包apk记得添加权限

  • { {flutterPorject}}\android\app\src\main\AndroidManifest.xml

  • manifet文件夹里

     <uses-permission android:name="android.permission.READ_PHONE_STATE" /> //访问电话状态
        <uses-permission android:name="android.permission.INTERNET" /> //允许全部网络访问
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> //获取网络信息状态
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> //获取当前WiFi接入的状态以及WLAN热点的信息
    

实战–》遇见坑

启动错误,出现卡住

  • 错误

Waiting for another flutter command to release the startup lock…

  • 解决
  • 打开后台,关闭所有的dart进程
  • 打开flutter安装目录/bin/cache删除lockfile文件
  • 打开越网
    • set PUB_HOSTED_URL=https://pub.flutter-io.cn
    • set FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
  • 可以使用flutter命令了。

父容器越界(超出)

image-20200502134903063
  • 解决

    //适合Android的布局
    import 'package:flutter/material.dart';
    import 'package:dio/dio.dart';
    
    class HomePage extends StatefulWidget{
      @override
      State createState() {
        // TODO: implement createState
        return HomePageState();
      }
    
    }
    
    class HomePageState extends State {
    //  声明数据:一定记得实例化
     TextEditingController inputValue = TextEditingController();
      @override
      Widget build(BuildContext context) {
        // TODO: implement build
        return Container(
          child: Scaffold(
            appBar: AppBar(title: Text("首页"),),
            body: SingleChildScrollView(
              child:  Column(
                children: [
                  TextField(
                    controller: inputValue ,
                    decoration: InputDecoration(
                        labelText: "提示文本",
                        helperText: "下边提示文本",
                        contentPadding: EdgeInsets.all(10)
                    ),
                    autofocus: false,
                  ),
                  RaisedButton(
                    child: Text("点击获取"),
                    onPressed: (){
                      getInput( context);
                    },
                  ),
                  Text("fdsa"),
                  Text("fdsa"),
                  Text("fdsa"),
                  Text("fdsa"),
                  Text("fdsa"),
                  Text("fdsa"),
                  Text("fdsa"),
                  Text("fdsa"),
                  Text("fdsa"),
                  Text("fdsa"),
                  Text("fdsa"),
                  Text("fdsa"),
                  Text("fdsa"),
                  Text("fdsa"),
                  Text("fdsa"),
                ],
              ),
            )
          ),
        );
      }
    //  测试输入框
      void getInput(context) {
        showDialog(
            context: context,
            builder: (context) => AlertDialog(
                  title: Text("文本框:"+inputValue.text.toString()),
                ));
      }
    
    //  准备一个测试网络的方法
      Future getHttp() async {
        try {
          Response response;
          response = await Dio().get("https://v1.hitokoto.cn/");
          return print(response);
        } catch (e) {
          return print(e);
        }
      }
    }
    

    注意:

    • 这里的Text是我故意越界
    • 在body布局开始时加上SingleChildScrollView
      • 里的child里布局
    • 会更ListView冲突

访问接口出现451问题

  • 问题解析

在计算机网上领域中,HTTP 451 因法律原因不可用(英语:HTTP 451 Unavailable For Legal Reasons)是一种HTTP协议的错误状态代码,当用户请求访问某个经政府审核等查核方法后认定不合法的来源时,就会显示这个错误代码。

  • 解决问题

    • 准备一个请求头—>接口里的---->Request Headers里全部

    • 创建一个dart文件

      const jikeHead = {
      'Accept': 'application/json, text/plain, */*',
      'Accept-Encoding':'gzip, deflate, br',
      'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
      'Connection': 'keep-alive',
      'Cookie': 'LF_ID=1588471987694-7544553-574131; _ga=GA1.2.60515761.1588471988; _gid=GA1.2.1683854147.1588471988; _gat=1; GRID=0b448de-8b6f706-eee4cd0-6806924; SERVERID=1fa1f330efedec1559b3abbcb6e30f50|1588472006|1588471988',
      'Host': 'live.geekbang.org',
      'Origin': 'https://time.geekbang.org',
      'Referer': 'https://time.geekbang.org/',
      'Sec-Fetch-Mode': 'cors',
      'Sec-Fetch-Site': 'same-site',
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36',
      };
      

      注意:

      格式是键值关系,并且是字符串.

    • 导入该文件

    //导入极客的头文件
    import 'package:flutter_electricity/config/httpHead.dart';
    
    • DIO请求时设置头
    Dio dio = new Dio();
    dio.options.headers = jikeHead;
    response = await dio.get("https://live.geekbang.org/serv/v1/stage/live?channel_id=1");
    

动态页面—>页面保持状态

问题

主要问题:页面不能保持,每次切换页面时,都会重新调用节课,并重新渲染

解决

  • 方法一

    • 首先修改主动态组件框架里的body
       body: IndexedStack(
              index: currentIndex,
              children: bodyList,
            ));
    
    • index:当前页面
    • children:所有页面组合
    • 把子页面混合一个AutomaticKeepAliveClientMixin并继承方法
    class HomePageState extends State with AutomaticKeepAliveClientMixin {
    //  混入动态界面保持状态
      @override
      // TODO: implement wantKeepAlive
      bool get wantKeepAlive => true;
    
  • 方法二

    • 直接在主动态组件框架里的body添加Offstage
    body: Stack(
        children: [
            Offstage(
                offstage: currentIndex != 0,
                child: tabBodies[0],
            ),
            Offstage(
                offstage: currentIndex != 1,
                child: tabBodies[1],
            ),
            Offstage(
                offstage: currentIndex != 2,
                child: tabBodies[2],
            ),
            Offstage(
                offstage: currentIndex != 3,
                child: tabBodies[3],
            ),
        ],
    ),
    
    • offstage:如果不是当前页面
    • child:当前页面

json对象转换为dart对象

与java中的Gost转为java对象差不多

  • 首先进入网站得到对象

    • https://javiercbk.github.io/json_to_dart/
    • 得到对象后复制到你都dart文件即可。
  • 其次代码转换即可

    //对象.formJson(json串)
    

flutter的Navigator 跳转不了页面

  • 错误

Navigator operation requested with a context that does not include a Navigator.

  • 问题
void main() => runApp(MyApp());	//入口
//-------
Widget build(BuildContext context) {	//主结构
    return MaterialApp(		//问题所在
        child: DefaultTabController(
            child: HomeWidget(pageList),
            length: pageList.length,
        ));
}
//-----跳转页面处
@override
void initState() {	//页面被创建时
    //跳转页面....
    // TODO: implement initState
    super.initState();
}
//------或
actions: [	//头部appbar中的按钮区域
IconButton(	
    onPressed: () {
        // 跳转登陆页面.....
    },
)]
  • 主要问题不能再MaterialApp的跟节点跳转,跟context有关系!
  • 解决
void main() => runApp(MaterialApp(home:MyApp()));
  • 只要把MaterialApp提在入口区域即可,其余代码在主结构里处理。

导入插件不知道版本号或版本冲突

dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.3
  chewie: any

使用any适应版本

flutter进阶

flutter遇到循环页面渲染

  • 例如

    return ListView(children: [
        Text("fds");
    ],)
    

    需要循环许多相同布局在children: 里

  • 实现

    return ListView(children: objs.map((item){
        return Text();
    })).toList();
    

    注意:

    • objs一定为list类型,dart语法中list就是数组.
    • map一定得指明类型,除了只需要String。
    • item就是objs的单列表对象

你可能感兴趣的:(fluuter,fluuter入门,前端新技术,前端)