环境
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 | 动态修改主题 )