环境
Flutter 3.29 macOS Sequoia 15.4.1 Xcode 16.3
控制器(ViewControllers) 在UIKit中,通过ViewController控制数据在视图上展现,多个ViewController组合在一起构建复杂的用户界面。在Flutter中,因为所有都是Widget,所以ViewController相关的功能也由Widget来承担。
生命周期事件 在UIKit中可以重写自定义控制器的生命周期的方法,或注册AppDelegate的回调。在Flutter3.13前,没有这个概念,但是可以通过监听WidgetsBinding
观察者和didChangeAppLifecycleState()
改变事件来实现
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 import 'package:flutter/material.dart' ;void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super .key}); @override Widget build(BuildContext context) { return const MaterialApp( home: Scaffold(body: Center(child: BindingObserver())), ); } } class BindingObserver extends StatefulWidget { const BindingObserver({super .key}); @override State<BindingObserver> createState() => _BindingObserverState(); } class _BindingObserverState extends State <BindingObserver > with WidgetsBindingObserver { @override void initState() { super .initState(); WidgetsBinding.instance.addObserver(this ); } @override void dispose() { WidgetsBinding.instance.removeObserver(this ); super .dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { super .didChangeAppLifecycleState(state); switch (state) { case AppLifecycleState.detached: _onDetached(); case AppLifecycleState.resumed: _onResumed(); case AppLifecycleState.inactive: _onInactive(); case AppLifecycleState.hidden: _onHidden(); case AppLifecycleState.paused: _onPaused(); } } void _onDetached() => print ('detached' ); void _onResumed() => print ('resumed' ); void _onInactive() => print ('inactive' ); void _onHidden() => print ('hidden' ); void _onPaused() => print ('paused' ); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("监听事件" )), body: Center(child: Text("生命周期" )), ); } }
Flutter 3.13后通过设置AppLifecycleListener
来实现响应生命周期变更的功能。
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 import 'package:flutter/material.dart' ;void main() { runApp(const MainApp()); } class MainApp extends StatelessWidget { const MainApp({super .key}); @override Widget build(BuildContext context) { return const MaterialApp( home: Scaffold(body: Center(child: BindingObserver())), ); } } class BindingObserver extends StatefulWidget { const BindingObserver({super .key}); @override State<BindingObserver> createState() => _BindingObserverState(); } class _BindingObserverState extends State <BindingObserver > { late final AppLifecycleListener _listener; @override void initState() { super .initState(); _listener = AppLifecycleListener(onStateChange: _onStateChanged); } void _onStateChanged(AppLifecycleState state) { switch (state) { case AppLifecycleState.detached: _onDetached(); case AppLifecycleState.resumed: _onResumed(); case AppLifecycleState.inactive: _onInactive(); case AppLifecycleState.hidden: _onHidden(); case AppLifecycleState.paused: _onPaused(); } } void _onDetached() => print ('detached' ); void _onResumed() => print ('resumed' ); void _onInactive() => print ('inactive' ); void _onHidden() => print ('hidden' ); void _onPaused() => print ('paused' ); @override void dispose() { _listener.dispose(); super .dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("监听事件" )), body: Center(child: Text("生命周期" )), ); } }
主题,样式和媒体 使用一个主题 Flutter 设置了一些主题,可以实现统一更改文本和 UI 组件的样式等。
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 import 'package:flutter/material.dart' ;void main() { runApp(const ThemePage()); } class ThemePage extends StatefulWidget { const ThemePage({super .key}); @override State<ThemePage> createState() => _ThemePageState(); } class _ThemePageState extends State <ThemePage > { Brightness brightness = Brightness.light; @override Widget build(BuildContext context) { return MaterialApp( title: "Theme 主题修改" , theme: ThemeData(brightness: brightness, primarySwatch: Colors.blue), home: Scaffold( appBar: AppBar(title: Text("Theme 主题修改" )), body: Column( children: [ ElevatedButton( onPressed: () { setState(() { brightness = Brightness.light; }); }, child: Text("切换到日间主题" ), ), ElevatedButton( onPressed: () { setState(() { brightness = Brightness.dark; }); }, child: Text("切换到夜间主题" ), ), ], ), ), ); } }
使用自定义字体 Flutter要使用自定义的字体可以在pubspec.yaml
文件中添加
1 2 3 4 fonts: - family: google_kavivanar fonts: - asset: static/font/google_kavivanar.ttf
1 2 3 4 Text( "Theme 123" , style: TextStyle(fontFamily: 'google_kavivanar' ), ),
字体样式 Text
widget有TextStyle
对象,可以通过这个属性来设置样式
color – 字体颜色
decoration – 修饰
decorationColor – 修饰颜色
decorationStyle – 装饰的样式
fontFamily – 字体
fontSize – 字体大小
fontStyle – 字体风格(斜体,正常)
fontWeight – 用于绘制文本的字形的厚度
wordSpacing – 词间距
letterSpacing – 字间距
height – 文本区域的高度
应用资源包 在Flutter中使用assets表示资源
在pubspec.yaml
声明资源
1 2 assets: - static/data.json
iOS中图片的资源是1.0x,2.0x,3.0x格式的,在Flutter中比如图片是在static/images下的,那不同倍数图片的存放方式。
1 2 3 static /images/my_icon.png static /images/2 .0x/my_icon.png static /images/3 .0x/my_icon.png
在pubspec.yaml
声明图片资源
1 2 assets: - static/images/my_icon.png
然后通过AssetImage或Image.asset来访问
1 2 3 4 5 6 image: AssetImage('static/images/my_image.png' ), @override Widget build(BuildContext context) { return Image.asset('static/images/my_image.png' ); }
表单输入 在UIKit中,通常是在提交时查询对应的输入框的当前值,因为Flutter的Widgets是不可变的,如何对用户的输入操作进行处理,
获取用户输入 针对TextFile
或TextFormField
,可以通过提供一个TextEditingController
来获取用户输入
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 import 'package:flutter/material.dart' ;void main() { runApp(MaterialApp(title: "TextFile" , home: MyForm())); } class MyForm extends StatefulWidget { const MyForm({super .key}); @override State<MyForm> createState() => _MyFormState(); } class _MyFormState extends State <MyForm > { final myController = TextEditingController(); @override void dispose() { myController.dispose(); super .dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Retrieve Text Input' )), body: Padding( padding: const EdgeInsets.all(16 ), child: TextField(controller: myController), ), floatingActionButton: FloatingActionButton( onPressed: () { showDialog( context: context, builder: (context) { return AlertDialog(content: Text(myController.text)); }, ); }, tooltip: 'Show me the value!' , child: const Icon(Icons.text_fields), ), ); } }
设置TextField的占位信息 1 2 3 Center( child: TextField(decoration: InputDecoration(hintText: 'textField占位信息' )), )
显示校验错误 在TextField的onSubmitted方法中,判断textField的输入是否合法,若不合法,可以在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 child: TextField( controller: myController, onSubmitted: (text) => { setState(() { if (!isEmail(text)) { _errorText = '错误: 邮箱地址不合法' ; } else { _errorText = null ; } }), }, decoration: InputDecoration( hintText: "邮箱" , errorText: _errorText, ), ),
参考
给 UIKit 开发者的 Flutter 指南
【Flutter】Flutter 应用主题 ( ThemeData | 动态修改主题 )