最近打算自己做一个电商 App 调研之后选择技术栈为 Flutter 因此打算梳理一下 Flutter 的所有知识点,做个预热。
- 关于自定义 widget 提示没有 key 我们可以通过下面的方式解决:
class GradientContainer extends StatelessWidget {
const GradientContainer({key}):super(key: key);
@override
Widget build(context){
return ...
}
}
其中,GradientContainer 相当于 constructor 而 {key} 相当于结构自动注入的入参,而 :super(key: key) 则是调用了父级的构造函数并将结构的 key 传入父级的构造函数。
或者使用更加简单的写法,如下所示:
class GradientContainer extends StatelessWidget {
const GradientContainer({super.key});
@override
Widget build(context){
return ...
}
}
-
代码划分,我们新建一个 dart 文件,然后将构建的 class 直接放入其中,并在原来的位置对其进行引用
- 构建 gradient_container.dart 文件
- 引入必要的依赖:
import 'package:flutter/material.dart'; class GradientContainer extends StatelessWidget { const GradientContainer({super.key}); @override Widget build(context){ return ... } }- 在原来的位置引入分离的代码并使用:
import 'package:flutter/material.dart'; import 'package:first_app/gradient_container.dart'; void main() { runApp( const MaterialApp( home: Scaffold( body: GradientContainer(), ) ) ) } -
使用 dart 中的变量 在上面的笔记中,我们完成了渐变色的设定,现在我们定义两个变量来完成此工作:
var startAlignment = Alignment.topLeft;
var endAlignment = Alignment.bottomRight;
当然定义变量的位置可以在 class 之外也可以在 class 之内,如下所示:
@override
Widget build(context) {
startAlignment = Alignment.topLeft;
return ...
}
在这种方式中,我们就没有在变量定义之前添加任何修饰符,私以为这是局部变量。
- 使用 var 定义的变量是可变的,而 const 定义的是不可变的,原来可变在某次赋值之后又不可变则使用 final:
const startAlignment = Alignment.topLeft;
const endAlignment = Alignment.bottomRight;
或者,
final startAlignment = Alignment.topLeft;
final endAlignment = Alignment.bottomRight;
通常在定义变量的时候直接初始化并使用 const 进行限制并不符合 finnal 的语义,因此上面的代码会被提示要更换成 const.(Use: 'const'for final variables initialized to a constant value. Try replacing 'final'with 'const'. )
- 类型构造参数 如下所示,我们封装组件要求其具有一定的灵活性,因此必须能够接受调用方传递的构造参数,接受参数的方式可以是位置传参或者命名传参,类似于上面 key 的传递:
const StyledText(String text, {super.key}); // Type annotations are added in front of parameters
上面采用的就是位置传参,并且在参数列表中我们使用 annotation 对形参的类型进行了限制.
此时,在调用方,参数应该传递为:styledText('Hello World!')
那么 key 又在那里呢?这里并没有传递 key 的值,其实 key 是可选的:You don't have to provide a value for the named key argument because named arguments, by default. are optional!
text 并不可以直接被使用,事实上,这里相当复杂,必须通过如下的方式让 class 内部能够 access 到这个值:
const StyledText(String text, {super.key}): outputText = text;
String outputText;
这样一来,我们就将形参 text 的值映射到 class 内部的 outputText 上,然后就可以在 class 内部使用了。
当然,这个参数和 key 本质上没有什么不同,因此也具有简写的方式:
StyledText(this.text,{super.key});
String text;
注意这里内部参数 text 和形参的名称必须是同名的。最佳实践是在其前面加上 final 如下所示:
StyledText(this.text,{super.key});
final String text;
- 传递复杂的构造参数 相比上面只传递一个 String 进来,下面我们尝试传递渐变色需要使用的颜色数组:
const startAlignment = Alignment.topLeft;
const endAlignment = Alignment.bottomRight;
...
...
...
const GradientContainer({super.key, required this.colors});
final List<Color> colors;
@override
Widget build(context){
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: colors,
begin: startAlignment,
end: endAlignment,
),
),
),
}
如果我们在 {} 中的形参前面加上 required 修饰,则表示此形参的值一定会被传递,这样就避免了 gradient 无法接受空值的问题。
当然如果只从解决问题的角度,我们可以将颜色值分开传递,就如同传递 text 一样:
const GradientContainer(this.color1, this.color2, {super.key});
final Color color1;
final Color color2;
@override
Widget build(context){
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [color1, color2],
begin: startAlignment,
end: endAlignment,
),
),
),
}
- 静态资源引入 dart, 例如图片
- 首先在根目录下面创建一个名为 asset 的目录,并在其中创建 images 子目录,然后将图片放入其中。
- 接下来在根目录的
pubspec.yaml文件中对其中的静态资源进行说明。在 pubspec.yaml 中找到 flutter 字段然后自动构建的项目应该有注释示范如何引入:
flutter:
uses-material-design: true
assets:
assets/images/dice-1.png
assets/images/dice-2.png
assets/images/dice-3.png
assets/images/dice-4.png
assets/images/dice-5.png
assets/images/dice-6.png
- 从引入图片到构造函数重载 dart 中自定义的类可以有多个构造函数,如下所示:
import 'package:flutter/material.dart';
class GradientContainer extends StatelessWidget {
final Color color1;
final Color color2;
const GradientContainer({Key? key, required this.color1, required this.color2}) : super(key: key);
const GradientContainer.purple({Key? key}) : this(
key: key,
color1: Colors.deepPurple,
color2: Colors.indigo,
);
/*
const GradientContainer.purple({super.key})
: color1= Colors.deepPurple,
color2 =Colors.indigo;
*/
@override
Widget build(BuildContext context) {
// 构建渐变容器的代码
}
}
上面的代码定义了两个重载的构造函数:
GradientContainer.purple
GradientContainer
这样当我们调用更加具体的构造函数的时候,将会获得更多的预设的参数,或者说可以传递较少的构造参数。
作为调用方,可以这样使用:
void main() {
runApp(
const MaterialApp(
home: Scaffold(
body: GradientContainer.purple(),
),
),
);
}
正是由于构造函数重载机制的存在,我们才能够以如下的方式加载图片:
child: Center(
child: Image.asset('assets/image/dice-2.png'),
),
- 布局组件 Row 和 Column 除了 Center 之外,我们还可以使用 Row 和 Column 进行布局,它们的介绍如下: 下面是以表格格式返回的信息:
| Layout Widget | Description | Main Axis | Cross Axis | Default Behavior |
|---|---|---|---|---|
| Column() | Can be used to place multiple child widgets next to each other | Vertical Axis | Horizontal Axis | Occupies the entire available height but only the width required by its content (children) |
| Row() | Can be used to place multiple child widgets next to each other | Horizontal Axis | Vertical Axis | Occupies the entire available width but only the height required by its content (children) |
这个表格概述了 Column() 和 Row() 布局小部件的主要用途、主轴和交叉轴的方向,以及它们的默认行为。
- 实现经典布局(图片 + 按钮)
child: Center(
child: Column(
children: [
Image.asset(
'assets/image/dice-2.png',
width: 200,
),
TextButton(onPressed: handlePressed, child: const Text('Roll Dice'),),
],
),
)
上面的布局思路就是,先将一个列放在中间然后再在此列中从上到下放图片和按钮,列宽由最大子组件撑开。
不难发现,我们现在需要在 dart 中定义一个函数,以此来完成点击事件的回调。