logo

前言

主要记录我在开发这个小说网项目的使用的代码块,方便忘记之后复用和分析

配置

  • 状态管理:Getx 以及一些工具类
  • 网络请求:dio
  • 缓存shared_preferences
  • 路由管理:fluro

环境变量

清华镜像

1
2
PUB_HOSTED_URL:https://mirrors.tuna.tsinghua.edu.cn/dart-pub
FLUTTER_STORAGE_BASE_URL:https://mirrors.tuna.tsinghua.edu.cn/flutter

官方镜像

1
2
3
window的用户直接将下面的添加到环境变量中
PUB_HOSTED_URL=https://pub.flutter-io.cn
FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

代码分析

常用布局

  1. Container: 用于创建矩形的可视化元素,可以设置背景颜色、边框、内外边距等属性。
  2. Row: 在水平方向上排列子控件。
  3. Column: 在垂直方向上排列子控件。
  4. Flex: 在主轴方向上创建弹性空间,通常与 RowColumn 一起使用。
  5. Wrap: 在水平或垂直方向上自动换行排列子控件。
  6. Stack: 叠放子控件,可以使用 Positioned 控制子控件的位置。
  7. Align: 在父控件内对齐子控件,可以设置对齐的位置。
  8. Center: 将子控件放置在中心位置。
  9. Expanded: 在 RowColumn 中,将子控件拉伸填充剩余空间。
  10. GridView: 创建一个网格布局。
  11. ListView: 创建一个可滚动的线性列表布局。
  12. SingleChildScrollView: 创建一个可滚动的单一子控件布局。
  13. Spacer: 在 RowColumn 中创建一个占位符。
  14. CustomScrollView: 创建自定义的滚动视图。
  15. SliverListSliverGrid: 在 CustomScrollView 中使用的滚动列表和网格布局。
  16. Flow: 类似于网格的方式排列子控件,支持设置子控件的宽高比。
  17. Table: 创建一个表格布局,适用于展示二维数据。
  18. AspectRatio: 调整子控件的宽高比。
  19. IntrinsicHeightIntrinsicWidth: 通过最大高度或最大宽度来约束子控件。
  20. FractionallySizedBox: 将子控件的大小设置为父控件的一部分比例。
  21. LimitedBox: 限制子控件的最大尺寸。
  22. Baseline: 根据基线对齐子控件。
  23. Positioned: 在父控件中精确定位子控件。
  24. IndexedStack: 叠放子控件,并根据索引显示其中一个。
  25. AspectRatio: 调整子控件的宽高比。
  26. ConstrainedBox: 对子控件施加额外的限制条件。
  27. FractionallySizedBox: 将子控件的大小设置为父控件的一部分比例。
  28. LayoutBuilder: 根据父控件的尺寸构建子控件。
  29. Offstage: 控制子控件是否可见,但仍占用空间。
  30. OverflowBox: 允许子控件超出父控件的边界。
  31. SizedBox: 固定尺寸的盒子。
  32. Transform: 对子控件进行旋转、平移、缩放等变换操作。

Fluro

记录一下fluro的用法吧

第一步

1
2
3
4
5
6
7
8
9

import 'package:fluro/fluro.dart';

// // 一些全局的类
class Global {
// 路由
static FluroRouter router = new FluroRouter();
}

第二步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
// 页面
import 'package:fictionapp/pages/home/home_page.dart';
import 'package:fictionapp/pages/login/login_page.dart';

import '../pages/login/register_page.dart';

// 登录页
var loginHandler = Handler(
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return const LoginPage();
});

// 首页
var HomeHandler = Handler(
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return const HomePage();
});

var RegisterHandler = Handler(
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
return const RegisterPage();
},
);

第三步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
import 'handlers.dart';

class Routes {
static String home = "/home";
static String login = "/login";
static String register = "/register";

static void configureRoutes(FluroRouter router) {
router.notFoundHandler = Handler(
handlerFunc: (BuildContext? context, Map<String, List<String>> params) {
print("ROUTE WAS NOT FOUND !!!");
return;
});
// 首页
router.define(home, handler: HomeHandler);
//登录
router.define(login, handler: loginHandler);
//注册
router.define(register, handler: RegisterHandler);
}
}

初始化

1
2
3
4
5
6
7
8
9
void main() {
// 初始化路由
FluroRouter router = new FluroRouter();
Routes.configureRoutes(router);
// 将初始化的路由放大全局组件中
Global.router = router;
runApp(const Application());

}
1
Global.router.navigateTo(context, "/register", transition: TransitionType.inFromRight);  // 具体页面的应用

列表组件

  • 列表最顶层的页面最好是构建 其余的全部打静止滚轮。
  • 自动刷新详见分类页的写法

getX

这边收藏一篇文章 写的非常好

GetBuilder

界面层在需要使用状态的地方使用 GetBuilder 包裹,然后就可以使用 Controller 访问状态对象和操作状态方法了。其中GetBuilder只需要两个参数:

init:初始状态对象,在这里可以完成状态对象的初始化。

builder 方法:这个方法用于构建依赖状态的组件树,方法携带状态对象参数,因此下面的组件可以访问到状态对象。而且一旦状态对象通过 update 方法通知有更新时,依赖状态对象的组件就会被刷新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Widget build(BuildContext context) {
return GetBuilder<CounterController>(
init: CounterController(),
builder: (controller) => Scaffold(
appBar: AppBar(
title: Text('GetX计数器'),
),
body: Center(
child: Text(
'${controller.counter}',
style: TextStyle(
color: Colors.blue,
fontSize: 24.0,
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
controller.increment();
},
),
),
);
}

以上代码有点问题,GetBuilder包住了整个Scaffold,我们没必要把不需要刷新的组件包住

可以改成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('GetX计数器'),
),
body: Center(
child: GetBuilder<CounterController>(
init: CounterController(),
builder: (_) => Text(
'${CounterController.to.counter}',
style: TextStyle(
color: Colors.blue,
fontSize: 24.0,
),
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
CounterController.to.increment();
},
),
);

当只包住需要刷新的Text组件时,按钮的点击方法和GetBuilder在同一个层级,没办法访问到buidler中的controller,这时候可以在controller中定义一个静态的别名方法

1
static CounterController get to => Get.find();

直接通过CounterController.to就可以访问到,但是要用Get.find()的话必须要先用Get.put()或者Get.LazyPut()注册依赖,然后就可以通过CounterController.to全局访问了

原文地址:https://www.cnblogs.com/r1cardo/p/17289339.html

设计一个自定义背景的底部导航

使用 Container 包装 Expanded,然后为 Container 设置背景颜色,以实现 “加入书架” 和 “缓存” 按钮在 BottomAppBar 中占满高度,使按钮垂直充满 BottomAppBar且具有自定义背景颜色的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
return BottomAppBar(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
child: TextButton(
onPressed: () {
// 处理导航项点击事件
// 您可以根据需要执行操作
},
child: Text(
'加入书架',
style: TextStyle(
color: Colors.blue,

),
),
),
),
Container(
width: 130,
color: Colors.blue,
child: Expanded(

child: TextButton(
onPressed: () {
// 处理导航项点击事件
// 您可以根据需要执行操作
},
child: Text(
'免费阅读',
style: TextStyle(
color: const Color.fromARGB(255, 252, 252, 253),

),
),
),
),
),
Expanded(
child: TextButton(
onPressed: () {
// 处理导航项点击事件
// 您可以根据需要执行操作
},
child: Text(
'缓存',
style: TextStyle(
color: Colors.blue,

),
),
),
),
],
),
);

状态栏

全局设置 放在main方法

1
2
3
4
5
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Color.fromARGB(0, 255, 255, 255), // transparent status bar
));

  1. WidgetsFlutterBinding.ensureInitialized();:这一行确保Flutter的绑定已经初始化。Flutter应用程序通常需要一些初始化工作,这个函数确保这些初始化工作已经完成。
  2. SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(...));:这行代码用于设置系统UI的覆盖样式,特别是状态栏的样式。在这里,它将状态栏的颜色设置为透明,使状态栏变成透明的样式,通常用于全屏应用程序。
1
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);

这个是隐藏,但是默认是黑色 需要调节

空安全

Dart的空安全是一种强类型语言特性,它有助于在编写代码时更好地处理可能为null的值。下面是一个关于Dart中使用空安全的String? fictionId的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void main() {
String? fictionId; // fictionId声明为可空类型

// 未赋值情况下,fictionId的值为null
print(fictionId); // 输出:null

// 赋予一个非空值
fictionId = "123";
print(fictionId); // 输出:123

// 使用条件运算符处理空值
String nonNullValue = fictionId ?? "默认值";
print(nonNullValue); // 输出:123

// 使用if语句检查空值
if (fictionId != null) {
print("fictionId有值:$fictionId");
} else {
print("fictionId为空");
}
}

在这个示例中,我们首先声明fictionId为可空类型(String?),它可以存储字符串或null。然后,我们演示了如何在不同情况下处理fictionId的值,包括条件运算符和if语句来检查是否为null。这有助于编写更安全的代码,避免空指针异常。

心得

  • 关于ListView非常消耗性能会造成卡顿 所以一个页面最多放一个ListView就可以。
  • 安装性能来说 优先考虑CustomScrollView()以及里面的的组件来搭建页面,方便快捷,性能明显提升。