Flutter中TextFormField的initialValue无法通过setState更新求助
问题:TextFormField无法从本地JSON更新初始值
尝试从本地文件读取JSON映射,将其中的serverIP值作为settings.dart中TextFormField的initialValue。但组件构建完成后,TextFormField不会随config变量的新值重建,initialValue始终显示"null"且无法更新。
相关代码
settings.dart
import 'package:flutter/material.dart'; import 'package:upm/file_manager.dart'; import 'package:upm/screens/home.dart'; import 'package:upm/screens/login.dart'; import 'package:upm/screens/signup.dart'; class Settings extends StatefulWidget { static const FileStorage storage = FileStorage(); const Settings({Key? key}) : super(key: key); @override State<Settings> createState() => _SettingsState(); } class _SettingsState extends State<Settings> { late Map<String, dynamic> config; @override void initState() { super.initState(); config = {}; getData().then((value) { setState(() { config = value; }); }); } Future<Map<String, dynamic>> getData() async { config = await const FileStorage().readConfig(); print("Ye lo config ${config["serverIP"]}"); return config; } @override Widget build(BuildContext context) { const String name = "Settings"; return Scaffold( appBar: AppBar( title: const Text(name), ), body: Container( padding: const EdgeInsets.only(left: 15, right: 15, bottom: 15, top: 15), child: Center( child: Column( children: [ TextFormField( initialValue: config["serverIP"].toString(), decoration: const InputDecoration( labelText: 'Server IP', border: OutlineInputBorder(), ), ), const SizedBox( height: 15, ), const TextField( decoration: InputDecoration( labelText: 'Server Port', border: OutlineInputBorder(), ), ), const SizedBox( height: 25, ), const ElevatedButton( onPressed: null, child: Text("Save"), ) ], ), ), ), drawer: Drawer( child: ListView( padding: EdgeInsets.zero, children: [ ListTile( title: const Text('Home'), onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => const Home()), ); }, ), ListTile( title: const Text('Sign In'), onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => const Login()), ); }, ), ListTile( title: const Text('Sign Up'), onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => const Signup()), ); }, ), ], ), ), ); } }
file_manager.dart
import 'dart:convert'; import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:path_provider/path_provider.dart'; class FileStorage extends StatelessWidget { const FileStorage({Key? key}) : super(key: key); Future<String> get _localPath async { final directory = await getApplicationDocumentsDirectory(); return directory.path; } Future<File> get _localFileConfig async { final path = await _localPath; if (await File("$path/config.ini").exists() == false) { File("$path/config.ini").create(); Map<String, dynamic> config = { "serverIP": "0.0.0.0", "port": 1234, }; writeConfig(config); } File file = File("$path/config.ini"); return file; } Future<Map<String, dynamic>> readConfig() async { try { final file = await _localFileConfig; String temp = await file.readAsString(); Map<String, dynamic> contents = await jsonDecode(temp); print("Ye Lo contents $contents"); return contents; } catch (e) { print("Ye bhi Parh Lo-T $e -T"); return <String, dynamic>{}; } } Future<File> writeConfig(Map<String, dynamic> config) async { final file = await _localFileConfig; return file.writeAsString(json.encode(config)); } @override Widget build(BuildContext context) { throw UnimplementedError(); } }
解决方案
1. 用TextEditingController替代initialValue
initialValue仅在组件首次初始化时生效,后续setState无法触发文本更新。改用控制器可以动态修改输入框内容:
修改_SettingsState类:
class _SettingsState extends State<Settings> { late Map<String, dynamic> config; late TextEditingController _serverIpController; @override void initState() { super.initState(); config = {}; _serverIpController = TextEditingController(); // 改用async/await让代码更清晰 _loadConfig(); } // 单独提取加载配置的方法 Future<void> _loadConfig() async { final loadedConfig = await const FileStorage().readConfig(); setState(() { config = loadedConfig; // 空值判断避免崩溃 _serverIpController.text = config["serverIP"]?.toString() ?? ""; }); } @override void dispose() { // 销毁控制器防止内存泄漏 _serverIpController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { // ... 其他代码不变 TextFormField( // 替换initialValue为controller controller: _serverIpController, decoration: const InputDecoration( labelText: 'Server IP', border: OutlineInputBorder(), ), ), // ... 其他代码不变 } }
2. 重构FileStorage为普通工具类
FileStorage不需要作为Widget使用,继承StatelessWidget完全多余,改成普通类还能避免不必要的错误:
修改file_manager.dart:
import 'dart:convert'; import 'dart:io'; import 'package:path_provider/path_provider.dart'; // 移除Widget继承 class FileStorage { const FileStorage(); Future<String> get _localPath async { final directory = await getApplicationDocumentsDirectory(); return directory.path; } Future<File> get _localFileConfig async { final path = await _localPath; final file = File("$path/config.ini"); if (!await file.exists()) { // 添加await确保文件创建完成 await file.create(); Map<String, dynamic> config = { "serverIP": "0.0.0.0", "port": 1234, }; // 添加await确保写入完成后再返回文件 await writeConfig(config); } return file; } Future<Map<String, dynamic>> readConfig() async { try { final file = await _localFileConfig; String temp = await file.readAsString(); // jsonDecode不是异步方法,不需要await Map<String, dynamic> contents = jsonDecode(temp); print("Ye Lo contents $contents"); return contents; } catch (e) { print("Ye bhi Parh Lo-T $e -T"); return <String, dynamic>{}; } } Future<File> writeConfig(Map<String, dynamic> config) async { final file = await _localFileConfig; return file.writeAsString(json.encode(config)); } }
3. 关键注意点
- 始终对异步操作添加
await,避免文件未创建/写入完成就执行读取操作; - 对
config["serverIP"]添加空值判断,防止空指针异常; - 记得在State销毁时释放
TextEditingController,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Ableds Gaming
相关产品推荐
相关产品推荐

