1、基础组件
文本,Text
按钮,TextButton等
图片图标,Image、Icon
输入框 和 表单,TextField、Form
进度指示器,LinearProgressIndicator、CircularProgressIndicator
单选框和复选框,Switch、Checkbox
2、布局之容器组件
装饰,DecoratedBox、BoxDecoration等,如颜色、背景、边框、渐变、阴影等
复合容器,Container,它是结合align、padding、margin、box、变换等多种功能。
尺寸,主要是设置页面的宽高大小 或 宽高比例。
宽高SizedBox、AspectRatio、LimitedBox、FractionallySizedBox等;
限制类ConstrainedBox、BoxConstraints、UnconstrainedBox等
对齐,Align
居中,Center
填充,Padding
剪裁,ClipOval、ClipPath、ClipRect、ClipRRect等
变换(如旋转等),Transform
适配(如换行,即超出父组件边界约束),FittedBox
3、布局组件
线性布局,Row、Column
弹性布局,Flex、Expanded
流式布局(子元素自动换行),Wrap、Flow
层叠布局,Stack、Positioned
4、复合组件(滚动、列表等)
简单滚动列表(类似iOS的ScrollView),SingleChildScrollView
列表,ListView、AnimatedList
二维列表(类似iOS的CollectionView),GridView
自定义滚动列表,CustomScrollView、NestedScrollView
TabBar,TabBarView
5、交互组件
手势,GestureDetector、GestureRecognizer等相关类
动画,Animation、Curve、AnimationController、Tween、Ticker等相关类
一、组件基类
abstract class DiagnosticableTree with Diagnosticable
abstract class Widget extends DiagnosticableTree
二、普通组件
abstract class StatelessWidget extends Widget
abstract class StatefulWidget extends Widget
// 状态
abstract class State<T extends StatefulWidget> with Diagnosticable
三、布局组件
// 布局类基类
abstract class RenderObjectWidget extends Widget
// 包含一个子Widget,如:ConstrainedBox、DecoratedBox等
abstract class SingleChildRenderObjectWidget extends RenderObjectWidget
通过属性child关联单个widget
// 包含多个子Widget,一般都有一个children参数,接受一个Widget数组。如Row、Column、Stack等
abstract class MultiChildRenderObjectWidget extends RenderObjectWidget
通过属性children关联多个widget。
// Widget树的叶子节点,用于没有子节点的widget,通常基础组件都属于这一类,如Image。
abstract class LeafRenderObjectWidget extends RenderObjectWidget
// Widget代理
abstract class ProxyWidget extends Widget
abstract class ParentDataWidget<T extends ParentData> extends ProxyWidget
// 弹性布局,flex
class Flex extends MultiChildRenderObjectWidget
class Flexible extends ParentDataWidget<FlexParentData>
class Expanded extends Flexible
// 线性布局
class Column extends Flex
class Row extends Flex
// 设置padding
class Padding extends SingleChildRenderObjectWidget
// SizedBox 设置宽高
class SizedBox extends SingleChildRenderObjectWidget
// Spacer 基于flex的空白,内部基于Expanded实现的,它只是Expanded的一个包装类
class Spacer extends StatelessWidget
// 流式布局
class Wrap extends MultiChildRenderObjectWidget
class Flow extends MultiChildRenderObjectWidget
// 层叠布局
class Stack extends MultiChildRenderObjectWidget
class Positioned extends ParentDataWidget<StackParentData>
// 对齐与相对定位
class Align extends SingleChildRenderObjectWidget
class Center extends Align
/* 容器类组件*/
// 填充
class Padding extends SingleChildRenderObjectWidget
// 尺寸大小限制
class ConstrainedBox extends SingleChildRenderObjectWidget
class SizedBox extends SingleChildRenderObjectWidget
class AspectRatio extends SingleChildRenderObjectWidget
class FractionallySizedBox extends SingleChildRenderObjectWidget
class LimitedBox extends SingleChildRenderObjectWidget
class UnconstrainedBox extends StatelessWidget
// 装饰类组件
class DecoratedBox extends SingleChildRenderObjectWidget
class BoxDecoration extends Decoration
// 组合类容器
class Container extends StatelessWidget
/* Material常用组件类 */
class Scaffold extends StatefulWidget
abstract class PreferredSizeWidget implements Widget
class AppBar extends StatefulWidget implements PreferredSizeWidget
class Drawer extends StatelessWidget
class FloatingActionButton extends StatelessWidget
class BottomNavigationBar extends StatefulWidget
class BottomNavigationBarItem
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
//使用字符串插值($或者${name}) 来打印包含变量的字符串
void main() {
String name = "Alice";
int age = 25;
print("My name is $name and I am $age years old.");
}
Dart 中 var 变量一旦赋值,类型便会确定,则不能再改变其类型
var t = "hi world";
// 下面代码在dart中会报错,因为变量t的类型已经确定为String,
// 类型一旦确定后则不能再更改其类型。
t = 1000;
dynamic与Object声明的变量都可以赋值任意对象,且后期可以改变赋值的类型,不同的是dynamic声明的对象编译器会提供所有可能的组合,而Object声明的对象只能使用 Object 的属性与方法, 否则编译器会报错
dynamic a;
Object b = "";
main() {
a = "";
printLengths();
}
printLengths() {
// 正常
print(a.length);
// 报错 The getter 'length' is not defined for the class 'Object'
print(b.length);
}
不可更改变量,使用 final 或 const,两者区别在于:const 变量是一个编译时常量(编译时直接替换为常量值),final变量在第一次使用时被初始化。被final或者const修饰的变量,变量类型可以省略
//可以省略String这个类型声明
final str = "hi world";
//final String str = "hi world";
const str1 = "hi world";
//const String str1 = "hi world";
int i = 8; //默认为不可空,必须在定义时初始化。
int? j; // 定义为可空类型,对于可空变量,我们在使用前必须判空。
// 如果我们预期变量不能为空,但在定义时不能确定其初始值,则可以加上late关键字,
// 表示会稍后初始化,但是在正式使用它之前必须得保证初始化过了,否则会报错
late int k;
k=9;
int age = 12;
double price = 10.0;
String name = "张飞";
String name2 = '张飞';//单双引号没区别
//多行字符串
String name3 = '''
张飞
诸葛亮
廉颇
''';
bool sex = false;
var nameList = ["张飞","关羽","刘备"];
List<String> nameList2 =["张飞","关羽","刘备"];
List<String> nameList3 = [];
var nameSet = {"张飞","关羽","刘备"};
Set<String> nameSet2 = {"张飞","关羽","刘备"};
Set<String> nameSet3 = {};
var map = {"name": "张飞", "height": 1.88};
Map<String, Object> map2 = {"name": "张飞", "height": 1.88};
Map<String, Object> map3 = {};
//无返回值
void fun1(){
}
//有返回值
int fun2(){
return 1;
}
//单行简写
int fun3()=>1;
//返回类型 void 和 int 也可以省略
注意, 只有可选参数才可以有默认值, 必须参数不能有默认值
//命名可选参数: {param1, param2, ...}
//位置可选参数: [param1, param2, ...]
//命名可选参数
optionParamsFun1(String name, {int age = 10, double height = 1.8}) {
print('$name,$age,$height');
}
//required 标识为必选参数
optionParamsFun2(String name, {int age = 10,required double height}{
print('$name,$age,$height');
}
//调用
optionParamsFun1('yh');
optionParamsFun1('yh',age: 20);
optionParamsFun1('yh',age: 20,height: 1.88);
// yh,10,1.8
// yh,20,1.8
// yh,20,1.88
//位置可选参数
optionParamsFun1(String name, [int age = 10, double height = 1.8]) {
print('$name,$age,$height');
}
optionParamsFun1('yh');
optionParamsFun1('yh', 20);
optionParamsFun1('yh', 20, 1.88);
// yh,10,1.8
// yh,20,1.8
// yh,20,1.88
//调用
test(){
getFunc();
test2(foo("张飞"));
}
//1.定义一个函数
foo(String name) {
print(name);
}
//2.将函数作为另一个函数的返回值
getFunc() {
return foo;
}
//3.将函数作为另一个函数的参数
test2(Function func) {
func("func");
}
void main3() {
var numbers = [1, 2, 3, 4, 5];
//遍历
numbers.forEach((element) {
print(element);
});
// 使用匿名函数对列表中的每个元素进行平方
var squaredNumbers = numbers.map((number) => number * number);
print(squaredNumbers.toList()); // 输出: [1, 4, 9, 16, 25]
}
var num = 7;
print(num / 3);//除 2.3333
print(num ~/ 3);//整除 2
print(num % 3);//取余 1
test1(){
//当变量不为null时使用变量,
var name = 'lucy';
name ??= 'sala';
print(name);//lucy
//当变量为null时,后面的值将被赋值给变量
var name2 = null;
name2 ??= 'Toms';
print(name2);//Toms
//如果a>10,b就等于a,否则b = 11
int a = 5;
int b = a>10?a:11;
}
//当变量不为null时使用变量,
var name = 'lucy';
print(name ?? 'sala');//lucy
//当变量为null时使用后面的值
var name2 = null;
print(name2 ?? 'Toms');//Toms
as 将对象强制转换为特定类型。
is 和 is! 判断变量是不是某种类型。
class Person {
String name = 'Tom';
void eat() {
}
void drink() {
}
void sleep() {
}
}
//..可以连续访问方法和属性(类似返回值为Person)
void test2(){
Person()..name..eat()..drink()..sleep();
}
void test2(){
List<String> names = ['Alice', 'Bob', 'Charlie'];
//普通遍历
for (int i = 0; i < names.length; i++) {
print(names[i]);
}
//forEach 遍历
names.forEach((name) {
print(name);
});
//遍历的同时转换为大写
List<String> upperCaseNames = names.map((name) => name.toUpperCase()).toList();
print(upperCaseNames);
//in 遍历
for (String name in names) {
print(name);
}
Set<String> mySet = {'A', 'B', 'C'};
for (var item in mySet) {
print(item);
}
mySet.forEach((item) {
print(item);
});
//转为list然后遍历
List<String> myList = mySet.toList();
for (var item in myList) {
print(item);
}
Map<String, dynamic> myMap = {'key1': 'value1', 'key2': 'value2'};
//遍历键值对
for (var entry in myMap.entries) {
print('${entry.key}: ${entry.value}');
}
//遍历键值对
myMap.forEach((key, value) => print('$key: $value'));
//遍历键
myMap.keys.forEach((key) => print(key));
//遍历值
myMap.values.forEach((value) => print(value));
}
while :先检查条件,再执行
int i = 0;
while (i < 5) {
print(i);
i++;
}
do-while :先执行,再检查
int i = 0;
do {
print(i);
i++;
} while (i < 5);
for循环中,我们可以使用"break"关键字来提前结束循环
for (int i = 0; i < 10; i++) {
if (i == 5) {
break;
}
print(i);
}
使用"continue"时,会跳过当前迭代的剩余代码,直接进入下一次迭代
for (int i = 0; i < 10; i++) {
if (i % 2 != 0) {
continue;
}
print(i);
}
void main(List<String> args) {
//实例化
var p1 = Person();
p1.eat();
}
class Person {
String name = 'Tom';
void eat() {
String name = 'lucy';
print(name);//lucy
print(this.name);//Tom
}
}
Dart为类提供了默认的构造函数,当有了自己的构造函数,默认的构造方法将会失效,
在Flutter中,构造方法有几种方式:默认、命名、工厂、常量
默认构造方法:这是最常见的构造方法,它没有任何参数。例如:
复制代码
class MyClass {
MyClass() {
// 构造方法的逻辑
}
}
命名构造方法:使用命名构造方法可以为类提供多个不同的构造方法,每个构造方法可以有不同的参数。例如:
class MyClass {
MyClass() {
// 默认构造方法
}
MyClass.namedConstructor() {
// 命名构造方法
}
}
在使用命名构造方法时,可以通过指定构造方法的名字来调用它。
带参数的构造方法
class MyClass {
int value;
MyClass(int value) {
this.value = value;
}
}
工厂构造方法:可以返回一个已经存在的实例,或者返回一个子类的实例。例如:
class MyClass {
factory MyClass() {
return _singleton;
}
MyClass._internal();
static final MyClass _singleton = MyClass._internal();
}
在这个例子中,工厂构造方法返回一个单例实例。
常量构造方法,成员变量必须是final修饰,使用const关键字实例化
class Person {
final String name;
final int age;
const Person(this.name,this.age);
}
void main(List<String> args) {
print(Colors.red);
//枚举属性 index索引,从0开始;values包含所有枚举值的list
print(Colors.red.index);
print(Colors.values);
}
enum Colors {
red,
green,
blue
}
//写法一
class Singleton {
const Singleton._internal();
factory Singleton() => const Singleton._internal();
}
Singleton single = Singleton();
//写法二
class Singleton {
Singleton._privateInstance();
static final Singleton instance = Singleton._privateInstance();
factory Singleton(){
return instance;
}
}
//使用
Singleton single = Singleton();
//写法三
class Singleton {
Singleton._internal();
static final Singleton _instance = Singleton._internal();
static Singleton getInstance() {
return _instance;
}
}
//使用
var instance = Singleton.getInstance();
//写法四
class Singleton {
// 私有化构造方法
Singleton._privateConstructor();
static final Singleton _instance = Singleton._privateConstructor();
//同下 : static Singleton get instance => _instance;
static Singleton get instance { return _instance;}
}
//使用
var instance = Singleton.instance;
//写法五
class Singleton {
Singleton._privateConstructor();
static final Singleton instance = Singleton._privateConstructor();
}
//使用
void mian(){
var instance = Singleton.instance;
var instance2 = Singleton.instance;
print(instance == instance2);
}
class Person {
var name;
set setName(String nameStr){
name = nameStr;
}
String get getName{
print('getName');
return name;
}
}
Person p = Person();
p.name = '123';
print(p.getName);//getName 123
class Person {
var name;
Person(this.name);
set setName(String nameStr){
name = nameStr;
}
String get getName{
print('getName');
return name;
}
eat() {
}
run(){
print('person run');
}
}
class Student extends Person {
int age;
Student.withAge(String name, this.age): super(name);
@override
run() {
// TODO: implement run
super.run();
print('student run');
}
}
test(){
Student s = Student.withAge('Tom', 18);
s.name = '123';
print(s.getName);//getName 123
s.run();
//person run
// student run
}
abstract class Animal {
eat();
run(){
}
}
class Dog extends Animal {
@override
eat() {
// TODO: implement eat
}
}
class SmallDog extends Dog {
@override
run() {
// TODO: implement run
}
}
Dart中的接口比较特殊, 没有一个专门的关键字来声明接口.
默认情况下,定义的每个类都相当于默认也声明了一个接口,可以由其他的类来实现(因为Dart不支持多继承)
class Eat {
eat() {
}
}
abstract class Run {
run() {
}
}
class Animal implements Eat,Run {
@override
run() {
// TODO: implement run
}
@override
eat() {
// TODO: implement eat
throw UnimplementedError();
}
}
除了通过class定义类,也可以通过mixin定义一个类。
通过mixin定义的类用于被其他类混入使用,通过with关键字来进行混入。
mixin Eat {
eat();
}
mixin Run {
run(){
}
}
class Animal with Eat,Run {
@override
eat() {
// TODO: implement eat
}
// @override
// run() {
// // TODO: implement run
// }
}
//泛型写法
// List
//_InternalLinkedHashMap
var list = ['a','b','c',123];
var dic = {'key1':'value1', 'key2':123};
//限制类型
var list2 = <String>['a','b','c'];
var dic2 = <String, String>{'key1':'value1', 'key2':'value2'};
//object
class Person {
Object name;
Object age;
Person(this.name, this.age);
}
Person p = Person('yh',18);
print(p.name.runtimeType);
print(p.age.runtimeType);
//T
class Person<T extends num> {
T age;
T height;
Person(this.age, this.height);
double sum(){
double s = (this.age as int) * (this.height as double);
return s;
}
}
Person p = Person(10,1.88);
print(p.age.runtimeType);// int
print(p.sum());// 18.799999999999997
//age可以为null
int? age = null;
age = 10;
//!表示age必然不为null
double height = age! * 10.0;
print(height);
常见的库URI有三种不同的形式
来自dart标准版,比如dart:io、dart:html、dart:math、dart:core(但是这个可以省略)
//dart:前缀表示Dart的标准库,如dart:io、dart:html、dart:math
import 'dart:io';
使用相对路径导入的库,通常指自己项目中定义的其他dart文件
//当然,你也可以用相对路径或绝对路径的dart文件来引用
import 'lib/student/student.dart';
Pub包管理工具管理的一些库,包括自己的配置以及一些第三方的库,通常使用前缀package
//Pub包管理系统中有很多功能强大、实用的库,可以使用前缀 package:
import 'package:flutter/material.dart';
库文件中内容的显示和隐藏:
如果希望只导入库中某些内容,或者刻意隐藏库里面某些内容,可以使用show和hide关键字
//show关键字:可以显示某个成员(屏蔽其他)
//hide关键字:可以隐藏某个成员(显示其他)
import 'lib/student/student.dart' show Student, Person;
import 'lib/student/student.dart' hide Person;
当各个库有命名冲突的时候,可以使用as关键字来使用命名空间
//库中内容和当前文件中的名字冲突
import 'lib/student/student.dart' as Stu;
Stu.Student s = new Stu.Student();
library关键字
通常在定义库时,我们可以使用library关键字给库起一个名字。
但目前我发现,库的名字并不影响导入,因为import语句用的是字符串URI
library math;
part关键字
在之前我们使用student.dart作为演练的时候,只是将该文件作为一个库。
在开发中,如果一个库文件太大,将所有内容保存到一个文件夹是不太合理的,我们有可能希望将这个库进行拆分,这个时候就可以使用part关键字了
不过官方已经不建议使用这种方式了:
mathUtils.dart文件
part of "utils.dart";
int sum(int num1, int num2) {
return num1 + num2;
}
dateUtils.dart文件
part of "utils.dart";
String dateFormat(DateTime date) {
return "2020-12-12";
}
utils.dart文件
part "mathUtils.dart";
part "dateUtils.dart";
test_libary.dart文件
import "lib/utils.dart";
main(List<String> args) {
print(sum(10, 20));
print(dateFormat(DateTime.now()));
}
export关键字
官方不推荐使用part关键字,那如果库非常大,如何进行管理呢?
将每一个dart文件作为库文件,使用export关键字在某个库文件中单独导入
mathUtils.dart文件
int sum(int num1, int num2) {
return num1 + num2;
}
dateUtils.dart文件
String dateFormat(DateTime date) {
return "2020-12-12";
}
utils.dart文件
library utils;
export "mathUtils.dart";
export "dateUtils.dart";
test_libary.dart文件
import "lib/utils.dart";
main(List<String> args) {
print(sum(10, 20));
print(dateFormat(DateTime.now()));
}
也可以通过Pub管理自己的库。
1、使用async/await语法:使用async关键字定义一个异步函数,并在需要进行异步操作的地方使用await关键字来等待异步操作的结果。例如:
Future<int> fetchData() async {
await Future.delayed(Duration(seconds: 1));
return 42;
}
void main() async {
print("Start");
int result = await fetchData();
print("Result: $result");
}
2、使用Future对象:可以使用Future对象来表示一个异步操作,并使用then方法来处理异步操作的结果。例如:
Future<int> fetchData() {
return Future.delayed(Duration(seconds: 1), () => 42);
}
void main() {
print("Start");
fetchData().then((result) {
print("Result: $result");
});
}
whenComplete:Future 完成(无论是成功还是失败)后调用该回调函数
Future<int> fetchSomeData() async {
// 模拟异步操作
await Future.delayed(Duration(seconds: 2));
// 返回一个成功的结果
return 42;
}
void main() {
fetchSomeData()
.then((value) {
print('成功获取到数据:$value');
})
.catchError((error) {
print('获取数据时发生错误:$error');
})
.whenComplete(() {
print('无论成功或失败,都会执行这个回调函数');
});
}
fetchSomeData 函数模拟了一个异步操作,它会在2秒后返回一个成功的结果。在 main 函数中,我们使用 .then 方法注册了一个成功时的回调函数,并使用 .catchError 方法注册了一个失败时的回调函数。最后,我们使用 .whenComplete 方法注册了一个无论成功或失败都会执行的回调函数。
3.使用Stream对象:Stream是一种用于处理异步数据流的对象,可以通过添加监听器来处理异步操作的结果。例如:
Stream<int> fetchData() {
return Stream.periodic(Duration(seconds: 1), (count) => count).take(3);
}
void main() {
print("Start");
fetchData().listen((result) {
print("Result: $result");
});
}
以上是Flutter中几种常见的实现异步操作的方式
在Flutter中,Stream和Future是用于处理异步操作的两种不同的机制。
Future是一种表示异步操作结果的对象。它通常用于表示一次性的操作,例如从网络获取数据或执行耗时任务。当一个Future对象被创建时,它立即开始执行,并且可以通过await关键字来等待其结果。Future只能返回一个结果,并且一旦结果可用,它将不会再次改变。您可以通过使用.then()方法或async/await语法来处理Future的结果。
Stream是一种表示一系列异步操作结果的对象。它通常用于表示实时或连续的数据流,例如从数据库查询结果或用户输入事件。当一个Stream对象被创建时,它并不会立即开始执行,而是每当有新的数据可用时,会通过事件的形式传递给监听器。您可以使用StreamSubscription来监听Stream的事件,并对每个事件做出相应的处理。
因此,Future适用于一次性的异步操作,而Stream适用于连续的异步数据流。在使用时,您可以根据具体需求来选择使用Future还是Stream。
Flutter中的compute函数是一个用于在Isolate中执行计算密集型任务的函数。Isolate是Dart中的一个概念,它类似于线程,但是具有独立的内存空间,可以并行执行任务。
使用compute函数,可以将计算密集型的任务从主线程中分离出来,以避免阻塞UI线程,从而提高应用的性能和响应能力。
下面是compute函数的基本用法示例:
import 'dart:async';
import 'package:flutter/foundation.dart';
Future<int> calculate(int n) async {
// 在这里执行计算密集型任务
int result = 0;
for (int i = 1; i <= n; i++) {
result += i;
}
return result;
}
void main() async {
// 使用compute函数在Isolate中执行计算任务
int result = await compute(calculate, 1000000);
print(result);
}
在上面的示例中,calculate函数是一个计算密集型的任务,它将对1到给定参数n之间的所有数字求和。main函数使用compute函数调用calculate函数,并传递一个参数1000000。compute函数会将calculate函数发送到一个新的Isolate中执行,并返回计算结果。
StatefulWidget和StatelessWidget是Flutter中用于构建用户界面的两种基本组件类型。
StatefulWidget是有状态的组件,它依赖于一个状态对象(State)来管理和维护数据。当这个状态对象发生变化时,StatefulWidget会触发重新构建,从而更新界面。StatefulWidget适用于需要根据数据动态更新界面的场景,比如表单输入、计数器等。
StatelessWidget是无状态的组件,它不依赖于任何状态对象,一旦创建就不会再改变。StatelessWidget适用于静态内容的展示,比如文本、图像等。
选择使用StatefulWidget还是StatelessWidget取决于你的界面是否需要动态更新。
如果界面的展示内容不随任何状态信息的变化而变化,可以选择使用StatelessWidget;
如果界面的展示内容需要根据数据动态更新,可以选择使用StatefulWidget。
在实际使用中,应根据具体情况来选择适合的组件类型。如果界面只有一些静态内容,没有交互和状态变化,可以使用StatelessWidget;如果界面需要有交互和状态变化,可以使用StatefulWidget。
需要注意的是,过度使用StatefulWidget可能会影响应用的渲染性能。因为StatefulWidget的更新会触发整个界面的重新构建,包括所有子组件的销毁和重建。所以在选择组件类型时,需要综合考虑界面的动态性和性能需求。
总结来说,
StatefulWidget适用于需要根据数据动态更新界面的场景,
而StatelessWidget适用于静态内容的展示。
正确选择组件类型可以提高应用的性能和开发效率。
Text(
'Hello, world!',
style: TextStyle(
fontSize: 20,
color: Colors.blue,
),
)
//RichText是用来展示富文本的组件,它支持多种样式的文本混合在一起展示
RichText(
text: TextSpan(
text: 'Hello',
style: TextStyle(color: Colors.black),
children: <TextSpan>[
TextSpan(
text: 'world',
style: TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(
text: '!',
style: TextStyle(color: Colors.red),
),
],
),
)
//允许用户选择和复制文本内容
SelectableText(
'Hello, World!',
style: TextStyle(fontSize: 20),
)
TextField(
decoration: InputDecoration(
labelText: '请输入文本',
),
)
//与TextField类似,但提供了更多的验证和表单功能。
Form(
child: TextFormField(
decoration: InputDecoration(
labelText: '请输入文本',
),
validator: (value) {
if (value.isEmpty) {
return '请输入文本';
}
return null;
},
),
)
//一个iOS风格的文本输入框控件
CupertinoTextField(
placeholder: '请输入文本',
)
//SearchDelegate是Flutter中的一个抽象类,可以用来实现搜索功能。
//要使用SearchDelegate,需要先创建一个继承自SearchDelegate的子类,并实现一些必要的方法。
class CustomSearchDelegate extends SearchDelegate {
@override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(Icons.clear),
onPressed: () {
query = '';
},
),
];
}
@override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () {
close(context, null);
},
);
}
@override
Widget buildResults(BuildContext context) {
// 在这里处理搜索结果的展示
return Container();
}
@override
Widget buildSuggestions(BuildContext context) {
// 在这里处理搜索建议的展示
return Container();
}
}
在buildActions方法中,我们可以添加一些操作按钮,比如清除搜索内容的按钮。buildLeading方法用于创建返回按钮。buildResults方法中可以展示搜索结果,buildSuggestions方法中可以展示搜索建议。
TextButton(
onPressed: () => print('TextButton pressed'),
child: Text('Btn'),
),
ElevatedButton(
onPressed: () => print('ElevatedButton pressed'),
child: Text('Btn'),
),
OutlinedButton(
onPressed: () => print('OutlinedButton pressed'),
child: Text('Btn'),
),
IconButton(
onPressed: () => print('IconButton pressed'),
icon: Icon(Icons.add),
)
TextButton、ElevatedButton、OutlinedButton和IconButton是Flutter中的不同类型的按钮控件,它们在外观和功能上有一些区别。
TextButton(文本按钮):TextButton是一个扁平化的按钮,没有背景颜色,默认透明。点击时会出现墨水飞溅效果。TextButton通常用于需要较为简洁和清晰的界面设计。
ElevatedButton(凸起按钮):ElevatedButton是一个凸起的按钮,带有一个默认的背景颜色。点击时会有一个升起的动画效果。ElevatedButton通常用于需要突出显示的操作,比如提交表单或重要的用户交互。
OutlinedButton(轮廓按钮):OutlinedButton是一个带有边框的按钮,没有背景颜色,默认透明。点击时会出现墨水飞溅效果。OutlinedButton通常用于需要较为轻量级的操作,比如取消或返回。
IconButton(图标按钮):IconButton是一个只包含图标的按钮,没有文字内容。IconButton通常用于需要仅使用图标表示操作的场景。
Dart 方方面面,▩Dart-可迭代集合
Flutter:组件Widget
Flutter 布局详解十篇
dart 和 Flutter ,Flutter:第三方常用库整理
推荐:Flutter学习笔记(汇总)