当前位置: 首页 > news >正文

Flutter - UIKit开发相关指南 - 控制器,主题,表单

环境

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()改变事件来实现

import 'package:flutter/material.dart';void main() {runApp(const MainApp());
}class MainApp extends StatelessWidget {const MainApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: Scaffold(body: Center(child: BindingObserver())),);}
}class BindingObserver extends StatefulWidget {const BindingObserver({super.key});State<BindingObserver> createState() => _BindingObserverState();
}class _BindingObserverState extends State<BindingObserver>with WidgetsBindingObserver {void initState() {super.initState();// 1.添加App事件变化的观察者WidgetsBinding.instance.addObserver(this);}void dispose() {WidgetsBinding.instance.removeObserver(this);super.dispose();}// 2.监听app生命周期变化的事件void didChangeAppLifecycleState(AppLifecycleState state) {super.didChangeAppLifecycleState(state);switch (state) {case AppLifecycleState.detached:_onDetached();/// On all platforms, this state indicates that the application is in the default running mode for a running application that has input focus and is visible./// 应用可见且能响应用户的输入,切回前台会触发case AppLifecycleState.resumed:_onResumed();/// At least one view of the application is visible, but none have input focus. The application is otherwise running normally./// 应用程序处于非活跃状态,并且未接收用户输入。此事件仅适用于 iOS,因为 Android 上没有对应的事件。/// 切到后台先触发这个方法case AppLifecycleState.inactive:_onInactive();/// All views of an application are hidden, either because the application is about to be paused (on iOS and Android), or because it has been minimized or placed on a desktop that is no longer visible (on non-web desktop), or is running in a window or tab that is no longer visible (on the web)./// 所有的应用视图被隐藏,或者应用被暂停case AppLifecycleState.hidden:_onHidden();/// The application is not currently visible to the user, and not responding to user input./// When the application is in this state, the engine will not call the [PlatformDispatcher.onBeginFrame] and [PlatformDispatcher.onDrawFrame] callbacks./// This state is only entered on iOS and Android./// 应用当前不可见,不响应用户的输入,但依然在后台运行,引擎不会回调PlatformDispatcher.onBeginFrame 和 PlatformDispatcher.onDrawFrame case AppLifecycleState.paused:_onPaused();}}void _onDetached() => print('detached');void _onResumed() => print('resumed');void _onInactive() => print('inactive');void _onHidden() => print('hidden');void _onPaused() => print('paused');Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("监听事件")),body: Center(child: Text("生命周期")),);}
}

2025-05-11 16.53.51.png

Flutter 3.13后通过设置AppLifecycleListener 来实现响应生命周期变更的功能。

import 'package:flutter/material.dart';void main() {runApp(const MainApp());
}class MainApp extends StatelessWidget {const MainApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: Scaffold(body: Center(child: BindingObserver())),);}
}class BindingObserver extends StatefulWidget {const BindingObserver({super.key});State<BindingObserver> createState() => _BindingObserverState();
}class _BindingObserverState extends State<BindingObserver> {/// 1. 定义观察者属性late final AppLifecycleListener _listener;void initState() {super.initState();/// 2. 添加App事件变化的观察者_listener = AppLifecycleListener(onStateChange: _onStateChanged);}/// 3. 回调方法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');void dispose() {// Do not forget to dispose the listener_listener.dispose();super.dispose();}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("监听事件")),body: Center(child: Text("生命周期")),);}
}

主题,样式和媒体

使用一个主题

Flutter 设置了一些主题,可以实现统一更改文本和 UI 组件的样式等。

import 'package:flutter/material.dart';void main() {runApp(const ThemePage());
}class ThemePage extends StatefulWidget {const ThemePage({super.key});State<ThemePage> createState() => _ThemePageState();
}class _ThemePageState extends State<ThemePage> {Brightness brightness = Brightness.light;Widget build(BuildContext context) {return MaterialApp(title: "Theme 主题修改",// 1. 创建主题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(// 2. 点击按钮触发onPressed: () {// 3. 通知FluttersetState(() {// 4. 设置亮度,更新主题brightness = Brightness.dark;});},child: Text("切换到夜间主题"),),],),),);}
}

202505121010.gif

使用自定义字体

Flutter要使用自定义的字体可以在pubspec.yaml文件中添加

fonts:- family: google_kavivanarfonts:- asset: static/font/google_kavivanar.ttf
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声明资源

assets:- static/data.json

iOS中图片的资源是1.0x,2.0x,3.0x格式的,在Flutter中比如图片是在static/images下的,那不同倍数图片的存放方式。

static/images/my_icon.png       
static/images/2.0x/my_icon.png  // 2.0x image
static/images/3.0x/my_icon.png  // 3.0x image

pubspec.yaml声明图片资源

assets:- static/images/my_icon.png

然后通过AssetImage或Image.asset来访问

image: AssetImage('static/images/my_image.png'),
Widget build(BuildContext context) {return Image.asset('static/images/my_image.png');
}

表单输入

在UIKit中,通常是在提交时查询对应的输入框的当前值,因为Flutter的Widgets是不可变的,如何对用户的输入操作进行处理,

获取用户输入

针对TextFileTextFormField,可以通过提供一个TextEditingController来获取用户输入

import 'package:flutter/material.dart';void main() {runApp(MaterialApp(title: "TextFile", home: MyForm()));
}class MyForm extends StatefulWidget {const MyForm({super.key});State<MyForm> createState() => _MyFormState();
}class _MyFormState extends State<MyForm> {// 1. 创建text控制器来获取textFiled的值final myController = TextEditingController();void dispose() {// 4. 清理生成的myControllermyController.dispose();super.dispose();}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Retrieve Text Input')),body: Padding(padding: const EdgeInsets.all(16),// 2.TextField绑定控制器child: TextField(controller: myController),),floatingActionButton: FloatingActionButton(/// 3. 当用户点击按钮时,弹出dialog,显示textField的值onPressed: () {showDialog(context: context,builder: (context) {return AlertDialog(content: Text(myController.text));},);},tooltip: 'Show me the value!',child: const Icon(Icons.text_fields),),);}
}

202505121435.gif

设置TextField的占位信息

Center(child: TextField(decoration: InputDecoration(hintText: 'textField占位信息')),
)

显示校验错误

在TextField的onSubmitted方法中,判断textField的输入是否合法,若不合法,可以在

child: TextField(controller: myController,// 1. 拦截事件onSubmitted:(text) => {// 2. 通知Flutter状态变更setState(() {// 3. 判断textField的内容是否合法,不合法则在build方法刷新时显示if (!isEmail(text)) {_errorText = '错误: 邮箱地址不合法';} else {_errorText = null;}}),},decoration: InputDecoration(hintText: "邮箱",errorText: _errorText,),
),

202505121502.gif

参考

  1. 给 UIKit 开发者的 Flutter 指南
  2. 【Flutter】Flutter 应用主题 ( ThemeData | 动态修改主题 )
http://www.xdnf.cn/news/411229.html

相关文章:

  • LTE信道估计MSEBER仿真-块状导频
  • 排查服务器内存空间预警思路
  • vLLM中paged attention算子分析
  • 防止网页被爬取的方法与第三方用户行为检测组件分析
  • 防火墙规则库详解
  • 基于STM32、HAL库的LPS22HBTR 气压传感器 驱动程序设计
  • 十三、动态对象创建(Dynamic Object Creation)
  • docker配置mysql主从同步
  • 无线定位之 三 SX1302 网关源码 thread_gps 线程详解
  • GF(2)域m次不可约及本原多项式的数量
  • Unity基础学习(十二)核心系统—物理系统之碰撞检测组件篇(1)刚体,碰撞体,材质
  • Tauri(2.5.1)+Leptos(0.7.8)开发桌面应用--程序启动界面
  • 深入掌握CSS Flex布局:从原理到实战
  • 数组作为指针计算大小时的误区
  • Android13 wifi设置关闭后断电重启会自动打开
  • JGEW-9液位流量压力温度实验装置
  • Genspark超级智能体调研
  • 从数据到洞察:解析结构化数据处理的智能跃迁
  • 苹果电脑笔记本macos Mac安装mixly 米思齐软件详细指南
  • 免费多线程下载工具
  • 电商物流的“速度与激情”:从城际运输到即时配送的全链路解析
  • 动态网站 LNMP
  • 每日Prompt:超现实交互场景
  • 全视通智慧病房无感巡视解决方案:科技赋能,重塑护理巡视新篇
  • 开关电源滤波器讲解
  • Cursor 配置 Browser MCP(基于浏览器底层协议控制)及浏览器插件安装
  • Blender 入门教程(一):模型创建
  • rust 全栈应用框架dioxus server
  • 大模型数据分析破局之路20250512
  • 架构、构架、结构、框架之间有什么区别?|系统设计|系统建模