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的情况,就是
环境还没有配置成功。
如果等的时间很久超过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
执行flutter config --no-analytics
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' } }
当然环境配置啥的还是使用Android studio,vscode只是用来编写代码,因为这个比较轻量级。
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)]
1.cd到我们的项目地址
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uPT2AcQN-1610685529244)(E:\Typora_text\images\flutter2.png)]
2.运行
flutter run
这里可能有点慢,这是在处理依赖。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UQcJGjC9-1610685529246)(E:\Typora_text\images\flutter3.png)]
出现这个代表运行成功了。
4.相关插件
//热更新:使用Dug运行可以实现ctrl+s热更新
r
//热重启
R
//显示网格
p
//Android和Ios视图切换
O
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RlHWOxTO-1610685529250)(E:\Typora_text\images\flutter4.png)]
其余功能都自带
//导入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:下划线样式
再主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:设置容器边框
//导入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:叠加颜色的模式
//导入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纵向
//导入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]}’:显示数据
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:数据样式
动态原理即可。
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:容器放入布局中的组件即可。
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:同样也是权重
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:定位布局,直接控制像素
// 实例化一个重叠布局为变量
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:分割线
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:传入上下文返回上一个页面
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}); //接收参数
- 显示参数使用${}即可显示
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异步请求)
#添加
assets:
#文件路径:根目录下的images
- images/
new Image.asset("images/img.png"),
修改软件名
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所有文件即可
当然,每个文件都有每个文件的大小。如果你有其他办法也可以使用
这里推荐一个easy-mock这个可以创建模拟接口给你测试(全网使用)
官网:https://www.easy-mock.com/login
功能:
- 创建url
- 创建接口get、post
这里考虑到没有,然后有很多坑
首先在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
// 准备一个测试网络的方法
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里自定义的
// 准备一个测试网络的方法
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是参数对象
// 使用json把字符串转为json对象
var data = json.decode(getHttp().data.toString());
注意这里只有Android打包
找到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即可。
storePassword=123456
keyPassword=123456
keyAlias=key
storeFile=D:/key.jks
def keystorePropertiesFile = rootProject.file("key.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
//-------
//android {
在android{前添加三行}(可能爆红,没关系)
//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
}
}
将上边代码替换为下边。
//命令
flutter build apk
生成后的地址
build---->app—>outpputs---->apk---->release---->app-release.apk
如果没有release文件夹可能时没签名成功。
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点击单个来切换页面。
这里考虑到没有,然后有很多坑
首先在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
//适合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 和后台服务一样,是获取请求回来参数对象
这样能减少项目开发的维护成本,以及统一性.
//封装url
const servceUrl = "http://106.13.185.176:7777/manage/"; //准备一个url
const servcePath={
'home':servceUrl+"getData" //获取主页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方法是动态布局中使用的,
- 它实现了双向绑定数据效果,修改数据,页面值也会修改。
//适合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
注意:
- FutureBuilder: 这个组件可以自动根据异步请求来渲染页面。
- future:需要给一个异步请求方法
- builder:构造方法,填入相应的逻辑
- Swiper:轮播组件
- itemBuilder:构造方法,轮播的容器,字布局
- itemCount:轮播字布局的个数
- pagination:是否有小图标—>SwiperPagination()构造
- autoplay:是否自动播放
//布局
@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),
注意一般初始化插件值初始化一次,所以放在第一次进入的界面
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参数书代表不会跟着系统字体改变
//适合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
注意:
- 获取数据返回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:里设置类型
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,),
);
}
}
添加一个图片组件即可
导入插件:import ‘package:url_launcher/url_launcher.dart’; //导入拨打电话获取发短信插件
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地址。
//商品推荐组件
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:设置边框
// 火爆商品的流水布局与网格布局差不多
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:字布局集合
// 自定义上拉刷新底部
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刷新的回调
这个是谷歌自己出来的一个插件,用于管理状态的。
可以理解为是一个双向数据绑定,而且是全局的。
双向数据绑定就是,数据改变页面也会跟着改变。
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
import 'package:flutter/material.dart';
//存入一个全局双向数据
class Counter with ChangeNotifier{
int value = 0;
increment(index){
value = index;
// 刷新数据
notifyListeners();
}
}
创建一个全局类,并准备一个修改属性的方法,方法得刷新。
//导入插件并添加依赖
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方法,这是官网的一个模板,不好解释,具体看文档;
//适合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来保持状态
import 'package:flutter/material.dart';
//存入一个全局双向数据
class StateRightRiCategoryNav with ChangeNotifier{
int index = 0;
increment(index){
this.index = index;
// 刷新数据
notifyListeners();
}
}
因为假数据的原因,这里我只保存了一个索引值
void main() {
// 绑定全局双向数据模板
var state_RightRiCategoryNav = StateRightRiCategoryNav();
var providers = Providers();
providers..provide(Provider.value(state_RightRiCategoryNav));
runApp(ProviderNode(child: MyApp(),providers: providers,));
}
这里按模板注入即可
//右侧二级导航
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状态管理来传入的
//适合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用于吐丝的一个插件
首先完成fluro框架
导入插件import ‘package:fluro/fluro.dart’;需要在pubspec.yml文件加入版本。
创建一个routers文件夹
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包下
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是模板
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-路径+参数
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转为实体对象,然后刷新
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方式注入即可
- 这是就可以开始使用了
return Container(
child: ListView(
children: [
DetailsTopArea(), //这是详细页面--头部
DetailsExplain(), //详细页面--通知
DetailsTabbar(), //切换标签
DetailsWeb() //显示切换页面
],
),
);
这里主要添加子页面
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值切换样式
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里的值显示即可
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();
}
}
注意:
- 这里用的都是静态的,模拟数据。但调用了接口
修改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布局才能点击
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();
},
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显示
//适合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 "调用获取购物车";
}
}
注意:
- 这里我们把一个页面拆分了好几个组件显示
子组件
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
),),
),
),
);
}
}
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,
),
),
)
],
),
);
}
}
//适合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命令了。
解决
//适合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冲突
在计算机网上领域中,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 = new Dio();
dio.options.headers = jikeHead;
response = await dio.get("https://live.geekbang.org/serv/v1/stage/live?channel_id=1");
主要问题:页面不能保持,每次切换页面时,都会重新调用节课,并重新渲染
方法一
body: IndexedStack(
index: currentIndex,
children: bodyList,
));
- index:当前页面
- children:所有页面组合
class HomePageState extends State with AutomaticKeepAliveClientMixin {
// 混入动态界面保持状态
@override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
方法二
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:当前页面
与java中的Gost转为java对象差不多
首先进入网站得到对象
其次代码转换即可
//对象.formJson(json串)
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适应版本
例如
return ListView(children: [
Text("fds");
],)
需要循环许多相同布局在children: 里
实现
return ListView(children: objs.map((item){
return Text();
})).toList();
注意:
- objs一定为list类型,dart语法中list就是数组.
- map一定得指明类型,除了只需要String。
- item就是objs的单列表对象