这篇文章来自我自己的有道云笔记 想看图片去那里
文档:Day 4_1 项目实践.md
链接:http://note.youdao.com/noteshare?id=1b97681ceb71e681d4b41c40ccdf4129&sub=5103CDAE33354B02AB269015C3FD36FB
如果你升级了Xcode这些软件 会导致这个运行不出来的问题
这个时候你就需要去 删除这个文件 让它重新加载 这样就可以 让它运行了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zYUzejnX-1588835661942)(C18F771E08EF47A4ADEEDBC6A8E9A804)]
进行调试的时候 发现原来的项目 不能跑起来了
大家以后再做类似的事情的时候 你同样会遇到同样的问题
这个东西是不断在升级的 所以 你需要经常去自己处理这种问题
这个问题可以去 flutter的issure上面找到答案
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xX1rfueB-1588835661948)(FF57D4532D254CD38FD8A61AC6807748)]
还有一个问题就是
之前我们使用extension它会有报错 所以我们可以到pubspce.yaml 里面去改变dart语法的最低支持版本
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qN8rgCfR-1588835661952)(697CA235BFF44C67A3BEDF41B848F58B)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dr4dBKPZ-1588835661958)(F6E91C0350CD416D9D474109A8E9344D)]
新建一个项目肯定是有些东西是要你来完成的
我们现在先来配置 Androidid
我们来到 Android 这个文件夹 这个东西就是Android的工程目录
我们来修改appid 我们打开这个 build.gradle
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gmz4FSdW-1588835661961)(49FA404B28014A33983C8C50CC85089B)]
可以看到这里有一个applicationId
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FWKwxV3u-1588835661964)(00639C7D253242B78708A6E297CFBF97)]
然后我们把它改了就是了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XpvFe0fO-1588835661966)(9703EFF8BB8847CA87829FDF67DC9C5D)]
然后我们发先我们打开这个文件之后它就在报错 主要是说没有找到这样的一个东西
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-th9HUaOE-1588835661968)(2E9B5EC2F6CB4D2C96342C0C490234F4)]
这个东西是 其实是不管它也是可以的 关掉这个文件 他还是在报错 这个时候我们把android Studio重启一下就可以了
但是你发现 这个应用程序名是默认的 项目名 你肯定不希望这个应用程序就是你的项目名
我们可以到 AndroidMainifest.xml文件里面去找对应的东西 这个是非常重要的一个文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dywWsDPk-1588835662001)(AE5662CA64C341D58E69C1D1529C7577)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Y4Hnf6R-1588835662008)(89A8D7F3372C4468915739B7F6150365)]
改掉
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hYTXG4AO-1588835662015)(3756881AA8164005A7126B4D56476985)]
我们的应用程序会有一个flutter默认提供的图标
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zWQ8kNCV-1588835662021)(57552D49EB274F66B978BC89DD556641)]
但是我们的自己的应用程序肯定不希望用这个图标
我们希望显示美工给我们的图标 那这里怎么办呢
你打开main文件夹 看到里面有很多的文件夹
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EJZo5Kfe-1588835662022)(C7B209160D95443698BEE3220CBC1D02)]
这些文件夹就是装的 不同大小的图标的 文件夹
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2sSzE9js-1588835662025)(FA372F6F95984B18A2B1ACC319962C04)]
这里有这样的一些东西
把里面的图标换成自己的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zfoTvuug-1588835662027)(09C1DE72C0CD4F9BB523D49534697385)]
这个图标的命名也是有讲究的
当然会android开发的就因该很明白
要搞启动图的话我们来到 layer-list 里面item就是里面的样式 就是白色的
所以我们这里最终呈现出来的就是白色
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IaFUGESm-1588835662029)(F0FB4425E15E4EF9AD03651B424A5A64)]
如果我们要 背景图片的话 我们可以打开下面的注释
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2MsXezEh-1588835662031)(9DE24D8EA695497D8B1BDB8954DA2687)]
我们可以发现 它也是加载一个mipmap里面的一个 launch_image 的图片
它就会去里面找到对应的
这个地方我们就搞了一张 如果对应分辨率找不到图片它就会到其他的分辨率去找 直到找到为止
所以我们这里可以只用放一张就行了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wb9W8icQ-1588835662033)(063C6B641020411499C4C98A118F98F3)]
对应的图片 同样是通过分辨率来进行区别的 这就是Android
我们可以在进入的页面上搞一个 新的页面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-spmi7cku-1588835662035)(FB1E5FAC470D45C2B67F1DB95BE8444A)]
设置一个定时器 然后 时间到了以后就push 进入正真的页面
跳转到另外一个页面 跳转到主要显示的页面就可以了
广告也还是比较简单的
还有一种就是引导页 可以使用pageView来使用
所以我们就把这些目录结构放到 我们的目录结构里面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3cR8PSkt-1588835662037)(F0C634B700584F2BAB189C0E28C4176E)]
但是这些也不是最终的目录结构 我们最终会生成很多的文件夹 这样的lib下面就会有很多的文件夹
这个时候全部都去lib里面去找其实也不是特别方便的 所以这里就进行了另外一层考虑
我们可以把目录结构划分的深一点 这样就可以说我们的目录结构 划分的比较好
我们会先搞两个文件
但是因为android Studio是开发Android的东西 它直接建文件夹认为你建的是包 这个时候看文件夹层次就不太好看 所以我们 直接在资源管理器里面把文件夹建好
这样我们功能相关的东西都放在上面 和页面相关的东西都放在下面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nzMWC63A-1588835662039)(5A8AA7EAA7354EFCB12A6EF5438B392C)]
这样划分的话就是这样的
main.dart的文件因该尽量简单
把项目中默认的生成的计数器案例给换了
甚至我们的Scaffold里面的home我们都不是不需要的 我们可以用initialRoute来代替
import "package:flutter/material.dart";
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter Demo",
theme: ThemeData(
primarySwatch: Colors.blue,
splashColor: Colors.transparent
),
initialRoute: ,
);
}
}
然后我们就先搞定 Theme这个东西
这里我们直接把之前封装好的 app_theme.dart 文件拿过来
当然这个我们就主要以普通模式作为开发目标了
我们发现每个页面都有一个相同的背景颜色
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5qQqyKta-1588835662041)(0642E1AFCC1D4F07B9A9ACE9BF2520E0)]
同样这个背景颜色也是可以在Theme里面设置的
使用canvasColor进行设置
sharded/app_theme.dart
import "package:flutter/material.dart";
class HYAppTheme {
// 1. 公共属性
static const double smallFontSize = 16;
static const double normalFontSize = 20;
static const double largeFontSize = 24;
// 2. 普通模式
static final Color normalTextColors = Colors.red;
// 可以在这里把它封装成一个方法 也可以封装成一个属性
static final ThemeData norTheme = ThemeData(
primarySwatch: Colors.pink, // primaryColor accentColor 如果不一样再单独设置
canvasColor: Color.fromRGBO(255, 254, 222, 1),
textTheme: TextTheme(
body1: TextStyle(fontSize: normalFontSize, color: normalTextColors)
)
);
// 3. 暗黑模式
static final Color darkTextColors = Colors.green;
static final ThemeData darkTheme = ThemeData(
primarySwatch: Colors.grey,
textTheme: TextTheme(
body1: TextStyle(fontSize: normalFontSize, color: darkTextColors)
)
);
}
main.dart
import "package:flutter/material.dart";
import 'package:project03/ui/shared/app_theme.dart';
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "美食广场",
theme: HYAppTheme.norTheme
);
}
}
还有一个就是关于我们的textTheme
我们知道textTheme里面有很多的属性
body1 默认的字体样式
body2
dispaly1
display2
display3
display4
当然我们希望能有一个共有默认字体大小 不想要的时候覆盖掉就行了
这里我们就不改它了
我们通过常量来对这些东西做一个规划(这样的好处是 不用在里面的繁杂代码里面去到处找颜色的设置)
import "package:flutter/material.dart";
class HYAppTheme {
// 1. 公共属性
static const double bodyFontSize = 14;
static const double smallFontSize = 16;
static const double normalFontSize = 20;
static const double largeFontSize = 24;
// 2. 普通模式
static final Color normalTextColors = Colors.red;
// 可以在这里把它封装成一个方法 也可以封装成一个属性
static final ThemeData norTheme = ThemeData(
primarySwatch: Colors.pink, // primaryColor accentColor 如果不一样再单独设置
canvasColor: Color.fromRGBO(255, 254, 222, 1),
textTheme: TextTheme(
body1: TextStyle(fontSize: bodyFontSize),
display1: TextStyle(fontSize: smallFontSize),
display2: TextStyle(fontSize: normalFontSize),
display3: TextStyle(fontSize: largeFontSize),
)
);
// 3. 暗黑模式
static final Color darkTextColors = Colors.green;
static final ThemeData darkTheme = ThemeData(
primarySwatch: Colors.grey,
textTheme: TextTheme(
body1: TextStyle(fontSize: normalFontSize, color: darkTextColors)
)
);
}
这个就是关于我们字体的一个东西
还有一个就是字体的颜色
这里主题就搞的差不多了
但是现在还是差点 我们Scaffold里面至少 home和initialRoute因该要有一个
但我们两个都没有所以我们的项目肯定是不能跑的
router/router.dart
import 'package:flutter/material.dart';
class HYRouter {
static final String initialRoute = "/";
static final Map routes ={
};
static final RouteFactory generateRoute = (settings) {
};
static final RouteFactory unknownRoute = (setting) {
};
}
我们默认要启动的页面就是 大的页面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZyGy3nPF-1588835662044)(F78015FAB5B04CA68CF992F86D6981ED)]
这个页面我们之前说douban 的时候就是一个main 它是一个主要的页面
同样把文件结构先搞出来
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-km0gxnme-1588835662046)(4618A9232BA848F49ABBDDAFAAB85C35)]
我们建立的这个main因该是一个有状态的 东西
所以我们使用StatefulWidget
构建整个类
import "package:flutter/material.dart";
class HYMainScreen extends StatefulWidget {
static const String routeName = "/";
@override
_HYMainScreenState createState() => _HYMainScreenState();
}
class _HYMainScreenState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
),
bottomNavigationBar: BottomNavigationBar(
items: BottomNavigationBarItem(
),
),
);
}
}
然后设置对应的 路由相关的东西
说命令 这个onGenerateRoute是怎么设置的
同样也可以 有错误页面的设置 unKownRoute怎么设置的
import 'package:flutter/material.dart';
import 'package:project03/ui/pages/main/main.dart';
class HYRouter {
static final String initialRoute = HYMainScreen.routeName;
static final Map routes ={
HYMainScreen.routeName: (ctx) => HYMainScreen()
};
// 自己扩展
static final RouteFactory generateRoute = (settings) {
return null;
};
static final RouteFactory unknownRoute = (setting) {
};
}
因为我们这几个东西都没有写
这样我们的主题和路由相关的东西就配好了
你会发现我们的MaterialApp里面是非常简单的
淡然还有一种可以把MyApp整个东西抽成一个文件 这样你就有一个文件可以专门来管理你的整个app了
但是我不喜欢这样所以不抽 这里的代码本来就已经比较复杂了
其实更不希望在lib的下面有更多的文件 我们希望我们的lib目录尽可能的简单
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m2tA6jUY-1588835662048)(780DA7A7771647929E16082FDDCEC350)]
所以我们比较希望这样搞
import "package:flutter/material.dart";
import 'package:project03/core/router/router.dart';
import 'package:project03/ui/shared/app_theme.dart';
main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "美食广场",
// 主题
theme: HYAppTheme.norTheme,
// 路由
initialRoute: HYRouter.initialRoute,
routes: HYRouter.routes,
onGenerateRoute: HYRouter.generateRoute,
onUnknownRoute: HYRouter.unknownRoute,
);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-krxn5mZf-1588835662050)(D32B65629E18422980E125303EA1B3C7)]
最主要的就是这几个页面的对应
我们这里的BottonNavigationBar 我们可以将它分到其他的地方
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bNwb6vC0-1588835662052)(37E74913556E4CDE86EDB88DFC68FD67)]
但是 一般感觉 这个地方代码只有这么长 我们一般感觉是没有必要 专门分一个文件吧
但是实际上我们开始 开发了之后 这个 文件容易
我们可以把它划分的精细一点 做的事情很单一 这样我们后期就不用代码重构 这样可以省下很多的整理代码的功夫
那具体要做某些事情的时候我们就找到某些文件 直接在文件中操作就会比较容易
import "package:flutter/material.dart";
final List pages = [];
final List items = [];
这些里面我们不希望再对它进行赋值的操作 所以加上
import "package:flutter/material.dart";
final List pages = [];
final List items = [
BottomNavigationBarItem(
title: Text("首页"),
icon: Icon(Icons.home)
),
BottomNavigationBarItem(
title: Text("收藏"),
icon: Icon(Icons.home)
)
];
然后我们创建两个页面
home.dart
import "package:flutter/material.dart";
class HYHomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("美食广场")
),
body: Center(child: Text("美食广场"))
);
}
}
favor.dart
import "package:flutter/material.dart";
class HYFavorScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("favor")
),
body: Center(child: Text("favor"))
);
}
}
main.dart
import "package:flutter/material.dart";
import "initialize_items.dart";
class HYMainScreen extends StatefulWidget {
static const String routeName = "/";
@override
_HYMainScreenState createState() => _HYMainScreenState();
}
class _HYMainScreenState extends State {
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: _currentIndex,
children: pages,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
items: items,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
),
);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZYn0bIzb-1588835662054)(5CFE2D7739E5436EB8C6A8846342A083)]
但是这个字体可能会一直变化
import "package:flutter/material.dart";
import "initialize_items.dart";
class HYMainScreen extends StatefulWidget {
static const String routeName = "/";
@override
_HYMainScreenState createState() => _HYMainScreenState();
}
class _HYMainScreenState extends State {
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: _currentIndex,
children: pages,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
items: items,
selectedFontSize: 14,
unselectedFontSize: 14,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
),
);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nS9q0X4v-1588835662057)(9924F5D562BF4B92B24A118F59CE2B47)]
这里有很多的数据 这些 页面数据我们就不通过网络请求来获得了 我们做成一个json文件来做json的解析
这里要补充一点json文件的解析
然后这个是一个资源 那我们就要对它进行配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w8Kzcki6-1588835662060)(4CE5C8E025CE4A0DB25C56553F1A2220)]
我们来到这个配置文件 注意这个前面有一个空格要删掉
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z1xnVVXX-1588835662150)(060C09FF670443B4A1CDD25737F7BDBE)]
这样才对
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q3IdSM43-1588835662151)(C130F715EB4F4961A93847A8844D525B)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yr3KKtii-1588835662153)(1544579FDF2348AC817CA10C3427C386)]
改成这样就可以了
同样注意 我们也要 执行pub get 命令
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0fZxrxc1-1588835662155)(A5DD96F230FC42ED96560CC3F474D21B)]
然后我们就要读取我们的json文件
加载完 之后就可以对json做一个解析
那我们在哪里进行加载呢 可以在build里面做吗 最好不要 这个里面 会经常执行 那么就
意味这我们要经常执行 请求 所以这个东西 我们就最好不要搞在这个build里面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qXJ3LmWL-1588835662157)(EF1F1358769546F9B71E01010C93E424)]
import "package:flutter/material.dart";
class HYHomeContent extends StatefulWidget {
@override
_HYHomeContentState createState() => _HYHomeContentState();
}
class _HYHomeContentState extends State {
@override
Widget build(BuildContext context) {
return Container();
}
}
如果 在homeScreen 有很多的地方要用 这个数据我们就可以这样使用 这个 如果我们的floatActionButton 也需要这个东西那我们 就需要将 这个HYHomeScreen 也变成stf了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T8wXLluO-1588835662159)(6C7423EA03434F6CAF4EA3E62E4FA227)]
这里我要做json请求那我们是直接请求吗 肯定是在initStatus里面进行请求的
import "package:flutter/material.dart";
class HYHomeContent extends StatefulWidget {
@override
_HYHomeContentState createState() => _HYHomeContentState();
}
class _HYHomeContentState extends State {
@override
void initState() {
// TODO: implement initState
super.initState();
// 加载数据
}
@override
Widget build(BuildContext context) {
return Container();
}
}
我们先到 services里面先去做一个处理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1LJSQV5P-1588835662162)(58E7BE16C702478191BC5F908C657C92)]
我们用rootBundle 来进行处理
import 'dart:convert';
import 'package:flutter/services.dart';
class JsonParse {
static void getCategoryData() async {
// 1. 加载json文件
// 它这里最终是返回了一个字符串 从字符串里面解析肯定是不好解析的
final jsonString = await rootBundle.loadString("assets/json/category.json");
// 2. 将我们的jsonString转化成Map/List
final result = json.decode(jsonString);
}
}
我们这里就拿到一个map了 我们这里最外面其实是一个对象 所以我们拿到的是一个map
当然转化成 map还不够 还要将里面的东西全部转化成模型
这个和我们之前讲的东西
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4G7sCJ0x-1588835662164)(91BC7FC374C441F49A3555C1063E1ADE)]
我们用 这个
来做 json转话成模型的快速生成代码
{
"id": "c1",
"title": "意大利",
"color": "9C27B0"
}
取个名字 就可以了 HYCategoryModel
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8x0my2n0-1588835662167)(2E28B9A4C4684B6C91DB857B49791C35)]
这里 其实就是一个map转 对象的过程
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:project03/core/model/category_model.dart';
class JsonParse {
static Future> getCategoryData() async {
// 1. 加载json文件
// 它这里最终是返回了一个字符串 从字符串里面解析肯定是不好解析的
final jsonString = await rootBundle.loadString("assets/json/category.json");
// 2. 将我们的jsonString转化成Map/List
final result = json.decode(jsonString);
// 3. 将Map中的内容转换成一个个对象
final resultList = result["category"];
// 我们可以一个一个转化成 模型
List categories = [];
for(var json in resultList ) {
categories.add(HYCategoryModel.fromJson(json));
}
return categories;
}
}
使用
import "package:flutter/material.dart";
import 'package:project03/core/services/json_parse.dart';
class HYHomeContent extends StatefulWidget {
@override
_HYHomeContentState createState() => _HYHomeContentState();
}
class _HYHomeContentState extends State {
@override
void initState() {
// TODO: implement initState
super.initState();
// 加载数据
JsonParse.getCategoryData().then((res) {
print(res);
});
}
@override
Widget build(BuildContext context) {
return Container();
}
}
成功加载出数据了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fsqEoT8I-1588835662169)(2AC94566D671472B90559B51A8F61521)]
取到数据之后就是展示
import "package:flutter/material.dart";
import 'package:project03/core/model/category_model.dart';
import 'package:project03/core/services/json_parse.dart';
class HYHomeContent extends StatefulWidget {
@override
_HYHomeContentState createState() => _HYHomeContentState();
}
class _HYHomeContentState extends State {
List _categories = [];
@override
void initState() {
// TODO: implement initState
super.initState();
// 加载数据
JsonParse.getCategoryData().then((res) {
_categories = res;
});
}
@override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: null,
itemBuilder: null
);
}
}
这个长度要给出来
@override
Widget build(BuildContext context) {
return GridView.builder(
itemCount: _categories.length,
gridDelegate: null,
itemBuilder: null
);
}
不给出来的话 默认是无限
展示 我们用GridView
import "package:flutter/material.dart";
import 'package:project03/core/model/category_model.dart';
import 'package:project03/core/services/json_parse.dart';
import "../../../core/extension/int_extension.dart";
class HYHomeContent extends StatefulWidget {
@override
_HYHomeContentState createState() => _HYHomeContentState();
}
class _HYHomeContentState extends State {
List _categories = [];
@override
void initState() {
// TODO: implement initState
super.initState();
// 加载数据
JsonParse.getCategoryData().then((res) {
_categories = res;
});
}
@override
Widget build(BuildContext context) {
return GridView.builder(
itemCount: _categories.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 20.px,
mainAxisSpacing: 20.px,
childAspectRatio: 1.5
),
itemBuilder: (ctx, index) {
return Text(_categories[index].title);
}
);
}
}
同样我们这里单位用了 px 所以extension走起 Sizefit注意要初始化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qls6wj9P-1588835662170)(43C1E66DC3DF4300891DA92CCE2EA074)]
文件结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KZhgRZwJ-1588835662172)(A431A477019B48E683200FC6574DD5B9)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EC3dvukX-1588835662174)(CA2C170DC1DA4AEF90ECA263F97D073D)]
这样我们就可以了
import "package:flutter/material.dart";
import 'package:project03/core/model/category_model.dart';
import 'package:project03/core/services/json_parse.dart';
import "../../../core/extension/int_extension.dart";
class HYHomeContent extends StatefulWidget {
@override
_HYHomeContentState createState() => _HYHomeContentState();
}
class _HYHomeContentState extends State {
List _categories = [];
@override
void initState() {
// TODO: implement initState
super.initState();
// 加载数据
JsonParse.getCategoryData().then((res) {
_categories = res;
});
}
@override
Widget build(BuildContext context) {
return GridView.builder(
padding: EdgeInsets.all(20.px),
itemCount: _categories.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 20.px,
mainAxisSpacing: 20.px,
childAspectRatio: 1.5
),
itemBuilder: (ctx, index) {
return Container(
color: Colors.red,
alignment: Alignment.center,
child: Text(_categories[index].title)
);
}
);
}
}
但是还是想要一个圆角
注意decoration 这个东西和 color冲突所以 将color放到里面
@override
Widget build(BuildContext context) {
return GridView.builder(
padding: EdgeInsets.all(20.px),
itemCount: _categories.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 20.px,
mainAxisSpacing: 20.px,
childAspectRatio: 1.5
),
itemBuilder: (ctx, index) {
return Container(
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(12.px)
),
alignment: Alignment.center,
child: Text(_categories[index].title)
);
}
);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GoE74YMC-1588835662176)(B17D283F2FA84001A7CAA58E2921399E)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MhBAStYK-1588835662178)(52F97971EC04402AB28C395DD7E1FEF2)]
看得出 这个东西是有一个渐变色的 颜色是不一样的
这个颜色是取决与哪里呢 很明显它不是一个随机的颜色
这个颜色取决于 我们的json的文件 那我们就需要将 这个转化成 颜色
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-liBZL2P6-1588835662180)(1BA37C8DB05149E484A23E4EFF7C77EA)]
如果用原来的 fromRGBO的方式 它还要做字符串截取
我们可以把它转化成 16进制
这个时候我们可以到 model里面去做处理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GQQ1h5gK-1588835662182)(39D5DCD6BEA645A08C90DA120835DFF7)]
import "package:flutter/material.dart";
class HYCategoryModel {
String id;
String title;
String color;
Color cColor;
HYCategoryModel({this.id, this.title, this.color});
HYCategoryModel.fromJson(Map json) {
id = json['id'];
title = json['title'];
color = json['color'];
// 1. 将我们的字符串Color转换成16进制的数字
final colorInt = int.parse(color, radix: 16);
// 2. 将透明度加进去
// 我们这里可以用一下 for运算符
cColor = Color(colorInt | 0xFF000000);
}
Map toJson() {
final Map data = new Map();
data['id'] = this.id;
data['title'] = this.title;
data['color'] = this.color;
return data;
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vEE9ewG1-1588835662185)(C17F47B8ED3F447CAF16C8D3137787A5)]
这样就可以了
然后我们这里还要做一个颜色的渐变
我们做渐变使用这个 LinearGradient Widgets
@override
Widget build(BuildContext context) {
return GridView.builder(
padding: EdgeInsets.all(20.px),
itemCount: _categories.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 20.px,
mainAxisSpacing: 20.px,
childAspectRatio: 1.5
),
itemBuilder: (ctx, index) {
final bgColor = _categories[index].cColor;
return Container(
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(12.px),
gradient: LinearGradient(
colors: [
bgColor.withOpacity(.5),
bgColor
]
)
),
alignment: Alignment.center,
child: Text(_categories[index].title)
);
}
);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lOaK4A2H-1588835662187)(F7A342C0FE3445C899EA9C07F8841510)]
微调一下样式
import "package:flutter/material.dart";
import 'package:project03/core/model/category_model.dart';
import 'package:project03/core/services/json_parse.dart';
import "../../../core/extension/int_extension.dart";
class HYHomeContent extends StatefulWidget {
@override
_HYHomeContentState createState() => _HYHomeContentState();
}
class _HYHomeContentState extends State {
List _categories = [];
@override
void initState() {
// TODO: implement initState
super.initState();
// 加载数据
JsonParse.getCategoryData().then((res) {
_categories = res;
});
}
@override
Widget build(BuildContext context) {
return GridView.builder(
padding: EdgeInsets.all(20.px),
itemCount: _categories.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 20.px,
mainAxisSpacing: 20.px,
childAspectRatio: 1.5
),
itemBuilder: (ctx, index) {
final bgColor = _categories[index].cColor;
return Container(
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(12.px),
gradient: LinearGradient(
colors: [
bgColor.withOpacity(.5),
bgColor
]
)
),
alignment: Alignment.center,
child: Text(
_categories[index].title,
style: Theme.of(context).textTheme.display2.copyWith(
fontWeight: FontWeight.bold
)
)
);
}
);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eA6swYaL-1588835662189)(1763215519EC4D4AA3383903D1B4567B)]
主题也重写一下 字体不好看
import "package:flutter/material.dart";
class HYAppTheme {
// 1. 公共属性
static const double bodyFontSize = 14;
static const double smallFontSize = 16;
static const double normalFontSize = 20;
static const double largeFontSize = 24;
// 2. 普通模式
static final Color normalTextColors = Colors.red;
// 可以在这里把它封装成一个方法 也可以封装成一个属性
static final ThemeData norTheme = ThemeData(
primarySwatch: Colors.pink, // primaryColor accentColor 如果不一样再单独设置
canvasColor: Color.fromRGBO(255, 254, 222, 1),
textTheme: TextTheme(
body1: TextStyle(fontSize: bodyFontSize),
display1: TextStyle(fontSize: smallFontSize, color: Colors.black87),
display2: TextStyle(fontSize: normalFontSize, color: Colors.black87),
display3: TextStyle(fontSize: largeFontSize, color: Colors.black87),
)
);
// 3. 暗黑模式
static final Color darkTextColors = Colors.green;
static final ThemeData darkTheme = ThemeData(
primarySwatch: Colors.grey,
textTheme: TextTheme(
body1: TextStyle(fontSize: normalFontSize, color: darkTextColors)
)
);
}
当主题改动的时候 我们要 hot restart
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fpjBTRiS-1588835662191)(440DD4035ED048EBB83A15203A9CD5AD)]