You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 18:49:13