1、Flutter 表单
Flutter中常见的表单有TextField单行文本框,TextField多行文本框、CheckBox、Radio、Switch
CheckboxListTile、RadioListTile、SwitchListTile、Slide。
TextField表单常见属性:
![](https://img.haomeiwen.com/i8621661/72bc26652e5b048c.png)
listview中常见TextField的边框设置无效,需要进行特别处理
class _HomePage extends State<HomePage> {
late String username;
late String password;
late TextEditingController textEditingController;
@override
void initState() {
super.initState();
//TextEditingController 配置初始化值
textEditingController =
TextEditingController.fromValue(const TextEditingValue(text: "贾先生"));
}
@override
Widget build(BuildContext context) {
return Center(
child: Padding(
padding: const EdgeInsets.all(20),
child: ListView(children: <Widget>[
TextField(
controller: textEditingController,
decoration: const InputDecoration(
hintText: "用户名", border: OutlineInputBorder()),
//获取输入框的值
onChanged: (value) {
setState(() {
username = value;
});
},
),
const SizedBox(height: 20),
TextField(
obscureText: true,
//获取输入框的值
onChanged: (value) {
setState(() {
password = value;
});
},
decoration: const InputDecoration(
hintText: "密码",
border: OutlineInputBorder(),
suffixIcon: Icon(Icons.visibility))),
const SizedBox(height: 20),
TextField(
decoration: InputDecoration(
hintText: "搜索",
//常规边框做法
// border: OutlineInputBorder(
// borderRadius: BorderRadius.circular(5),
// borderSide: const BorderSide(
// width: 1,
// color: Colors.red,
// style: BorderStyle.solid)),
//列表中边框特别处理
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: const BorderSide(
color: Colors.red,
width: 0.5,
style: BorderStyle.solid)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: const BorderSide(
color: Colors.red,
width: 0.5,
style: BorderStyle.solid)),
suffixIcon: const Icon(Icons.close))),
const SizedBox(height: 40),
const TextField(
decoration: InputDecoration(
icon: Icon(Icons.add),
prefixIcon: Icon(Icons.lock),
prefixText: "http://")),
]),
),
);
}
2、Radio单选按钮组及RadioListTile单选按钮组
class _RadioPagerState extends State<RadioPager> {
int sex = 1;
changeStatus(value) {
setState(() {
sex = value;
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
children: [
const Text( "男",style: TextStyle(fontSize: 19),),
Radio(value: 1, groupValue: sex, onChanged: changeStatus),
const SizedBox(width: 20,),
const Text("女"),
Radio(value: 2, groupValue: sex, onChanged: changeStatus),
],
),
const SizedBox(height: 20,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text(sex == 1 ? "男" : "女"),],
),
const SizedBox(height: 20,),
RadioListTile(
title: const Text("男"),
value: 1,
groupValue: sex,
onChanged: changeStatus),
RadioListTile(
title: const Text("女"),
value: 2,
groupValue: sex,
onChanged: changeStatus),
]),
);
}
}
3、Radio单选按钮组及RadioListTile单选按钮组
\\Radio单选及switch
Checkbox(value: flag, onChanged: changeStatus),
Switch(
value: flag,
onChanged: (v) {
setState(() {
flag = v;
});
},
),
\\RadioListTile单选按钮组
class _RadioPagerState extends State<RadioPager> {
final List hobby = [
{"checked": true, "title": "吃饭"},
{"checked": false, "title": "睡觉"},
{"checked": true, "title": "写代码"}
];
List<Widget> getHobby() {
List<Widget> tempList = [];
for (var i = 0; i < hobby.length; i++) {
tempList.add(CheckboxListTile(
value: hobby[i]["checked"],
title: Text(hobby[i]["title"]),
onChanged: (value) {
setState(() {
hobby[i]["checked"] = value;
});
}));
}
return tempList;
}
@override
Widget build(BuildContext context) {
return Center(
child: ListView(
children: [
Column(children: getHobby(),),
ElevatedButton(
onPressed: () {
print(hobby);
},
child: const Text("获取数据"))
],
),
);
}
}
4、进度条
children: [
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.red),
backgroundColor: Colors.white,
strokeWidth: 5,
),
SizedBox( height: 20,),
LinearProgressIndicator(
valueColor: AlwaysStoppedAnimation(Colors.red),
backgroundColor: Colors.white,
),
SizedBox(height: 20,),
LinearProgressIndicator(
value: 0.9,
valueColor: AlwaysStoppedAnimation(Colors.blue),
backgroundColor: Colors.grey,
),
SizedBox( height: 20,),
CupertinoActivityIndicator(
radius: 50,
),
]
5、Future
Future 是一个泛型,其中T代表的是我们耗时操作返回的具体值,如Future 表示一个未来的字符串,
Future表示一个未来的布尔值,如果不需要返回值,可以使用Future。
class _ProgressPagerState extends State<ProgressPager> {
int number = 0;
Future<int> getNetworkData() {
// 返回一个指定值的Future
// return Future.value("测试");
// 返回一个延时执行的Future
// return Future.delayed(const Duration(milliseconds: 2000), () {
// return "你好flutter";
// });
// 返回异步的function
return Future(() {
return 123;
});
}
void incrementCounter() {
getNetworkData()
.then((value) {
return value * 2;
})
.then((value) => print(value))
.whenComplete(() => print("完成"));
print("执行");
setState(() {
number++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("异步"),
),
floatingActionButton: FloatingActionButton(
onPressed: incrementCounter,
child: const Icon(Icons.add),
),
body: Center(
child: Text("$number"),
),
);
}
}
async和await关键字来处理future 异步变同步
作用:
- async:在方法体前面是使用,定义该方法为一个异步方法。
- await:等待并获得异步表达式的执行结果,并且给关键字只能在async修饰的方法中。
void incrementCounter() async {
var retult = await getNetworkData();
print(retult);
setState(() {
number++;
});
}
6、FutureBuilder
FutureBuilder是一个可以自动追踪Future的状态并在其状态改变的时候自动重绘的组件。
class _ProgressPagerState extends State<ProgressPager> {
Future<String> loadData() async {
await Future.delayed(const Duration(milliseconds: 2000));
// throw "404 data not found"; // 若需测试异常情况,可把注释去掉
return "this is server data"; // 正常返回数据
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("异步"),
),
body: FutureBuilder(
future: loadData(),
initialData: "我是一个初始值",
builder: (context, snapshot) {
// if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return Center(child: Text("Error:${snapshot.error}"));
} else {
return Center(child: Text("Data:${snapshot.data}"));
}
// } else {
// return const Center(
// child: CircularProgressIndicator(),
// );
// }
},
),
);
}
}
7、Stream 、StreamBuilder
Future在未来只会获取一个值。Stream的字面意思是水流,
Stream不像Future那样只会在未来获取一个值,它可以异步获取0个或者多个值。如果说Future是一个
异步版本的int或者String,Stream则更像是异步版本的列表,List,List,列表里面可能会有0个或者多个
元素。
StreamBuilder
在 Flutter 中,StreamBuilder 是一个将 Stream 流与 Widget结合到一起的组件,可实现组件的局部数
据更新 , StreamBuilder 组件和FutureBuilder组件比较相似,不同点在于它是一个可以自动跟踪
Stream(数据流或事件流)的状态,并在Stream有变化时自动重绘的组件。Stream不同于Future,可
能会在生命周期内释放出任意数量的数据值(正常)或者错误信息(异常),通常被用于读取文件或者
下载网络资源等操作,也有时候用于状态管理。
StreamBuilder主要功能:
1、实现局部刷新
2、读取流实现读取文件或者下载网络资源等操作
3、父子组件之间的数据广播
Stream<int> counter() {
return Stream.periodic(const Duration(milliseconds: 2000), (value) {
return value;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("异步"),
),
body: StreamBuilder(
stream: counter(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return const Center(
child: Text("NONE: 没有数据流"),
);
case ConnectionState.waiting:
return const Center(child: Text("WAITING: 等待数据流"));
case ConnectionState.active:
if (snapshot.hasError) {
return Center(
child: Text("ACTIVE: 数据流活跃,异常: ${snapshot.error}"));
} else {
return Center(
child: Text("ACTIVE: 数据流活跃,数据: ${snapshot.data}"));
}
case ConnectionState.done:
return const Center(child: Text("DONE: 数据流关闭"));
default:
throw "ConnectionState没有别的状态";
}
},
),
);
}
StreamController
一般情况我们都是监听别人给我们的数据流,比如 File("").openRead() 读取文件, 刚才给大家讲了
Stream.periodic 可以创建数据流,如果我们想创建更精确的数据流的话也可以使用
StreamController
注意: import 'dart:async'; // 需要导入异步包
final StreamController streamController = StreamController.broadcast();
@override
void initState() {
super.initState();
streamController.stream.listen((event) {
print(event);
});
}
@override
void dispose() {
super.dispose();
streamController.close();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("异步"),
),
body: Center(
child: Column(children: [
ElevatedButton(
// 按钮点击后Stream会释放出数字1
child: const Text("Emit 1"),
onPressed: () => streamController.add(1),
),
ElevatedButton(
child: const Text("Emit 2"),
onPressed: () => streamController.add(2),
),
ElevatedButton(
child: const Text("Emit 3"),
onPressed: () => streamController.add(3),
),
ElevatedButton(
child: const Text("Emit 4"),
onPressed: () => streamController.add(4),
),
ElevatedButton(
child: const Text("Emit 5"),
onPressed: () => streamController.add(5),
),
ElevatedButton(
child: const Text("Emit 6"),
onPressed: () => streamController.add(6),
),
ElevatedButton(
child: const Text("Emit 6"),
onPressed: () => streamController.add(6),
),
ElevatedButton(
// 按钮点击后Stream会释放出一个错误
child: const Text("Emit Error"),
onPressed: () => streamController.addError("oops"),
),
ElevatedButton(
// 按钮点击后Stream会关闭
child: const Text("Close"),
onPressed: () => streamController.close(),
),
StreamBuilder(
stream: streamController.stream
.where((event) => event > 3) //筛选大于3的
.map((event) => event * 2) //第一步筛选大于3的,第二步得到的值再*2
.distinct(), //去重
builder: (context, snapshot) {
print("正在重新绘制StreamBuilder组件…");
if (snapshot.connectionState == ConnectionState.done) {
return const Text("数据流已关闭");
}
if (snapshot.hasError) return Text("${snapshot.error}");
if (snapshot.hasData) return Text("${snapshot.data}");
return const Center(
child: CircularProgressIndicator(),
);
},
)
]),
),
);
}
8、Flutter StreamBuilder AnimatedBuilder 实现一个打字小游戏
class GamePager extends StatefulWidget {
const GamePager({super.key});
@override
State<GamePager> createState() => _GamePagerState();
}
class _GamePagerState extends State<GamePager> {
final StreamController<int> inputController = StreamController.broadcast();
final StreamController<int> scoreController = StreamController.broadcast();
int score = 0;
@override
void dispose() {
super.dispose();
inputController.close();
scoreController.close();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: StreamBuilder(
stream: scoreController.stream,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text("Error:${snapshot.error}");
}
if (snapshot.hasData) {
score = score + (snapshot.data as int);
return Text("得分:$score");
}
return const Text("等待输入");
}),
),
body: Stack(
children: [
...List.generate(3, (index) {
return Game(
inputController: inputController,
scoreController: scoreController);
}),
Align(
alignment: Alignment.bottomCenter,
child: KeyPad(inputController: inputController),
)
],
),
);
}
}
//算数 游戏 子组件
class Game extends StatefulWidget {
final StreamController<int> inputController;
final StreamController<int> scoreController;
const Game(
{super.key,
required this.inputController,
required this.scoreController});
@override
State<Game> createState() => _GameState();
}
class _GameState extends State<Game> with SingleTickerProviderStateMixin {
late int a, b;
late double x;
late AnimationController _animationController;
late Color color;
@override
void reset() {
a = Random().nextInt(5) + 1;
b = Random().nextInt(5);
x = Random().nextDouble() * 320;
color = Colors.primaries[Random().nextInt(Colors.primaries.length)];
}
@override
void initState() {
super.initState();
reset();
_animationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: Random().nextInt(5000) + 5000));
_animationController.forward(); //执行一次动画
_animationController.addStatusListener((status) {
if (status == AnimationStatus.completed) {
//监听动画执行完成
//生成随机数
reset();
widget.scoreController.add(-1); //最底部减去1分
_animationController.forward(from: 0.0); //重新执行一次动画
}
});
//监听键盘按键
widget.inputController.stream.listen((event) {
print(event);
if (a + b == event) {
reset();
widget.scoreController.add(3); //打中加3分
_animationController.forward(from: 0.0);
}
});
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
return Positioned(
top: Tween(begin: -50.0, end: 560.0)
.animate(_animationController)
.value,
left: x,
child: Container(
padding: const EdgeInsets.fromLTRB(8, 5, 8, 5),
decoration: BoxDecoration(
color: color, borderRadius: BorderRadius.circular(18)),
child: Text("$a+$b=?", style: TextStyle(fontSize: 24)),
),
);
});
}
}
//键盘 子组件
class KeyPad extends StatelessWidget {
final StreamController<int> inputController;
const KeyPad({super.key, required this.inputController});
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.bottomCenter,
child: Container(
color: Colors.red,
child: GridView.count(
shrinkWrap: true, //收缩
crossAxisCount: 3,
childAspectRatio: 5 / 2,
children: List.generate(9, (index) {
return TextButton(
style: ButtonStyle(
shape: MaterialStateProperty.all(
const RoundedRectangleBorder()),
backgroundColor:
MaterialStateProperty.all(Colors.primaries[index][300]),
foregroundColor: MaterialStateProperty.all(Colors.black)),
onPressed: () {
inputController.add(index + 1);
},
child: Text("${index + 1}",
style: Theme.of(context).textTheme.headline4));
}),
),
),
);
}
}
网友评论