Flutter Redux代码异常:无法展示本地Live Server的JSON数据
问题:Flutter Redux无法从本地Live Server加载JSON数据并展示
我跟着YouTube上的Flutter Redux教程学习,教程代码能正常运行,但自己实现后遇到逻辑问题——无法从本地Live Server的JSON文件获取数据并在App中展示。以下是我的代码及对应的people.json文件,请求帮忙排查问题。
我的代码
import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:redux/redux.dart'; import 'package:flutter_redux/flutter_redux.dart'; const apiUrl = 'http://127.0.0.1:5500/api/people.json'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: const HomePage(), ); } } @immutable class Person { final String name; final int age; const Person({required this.name, required this.age}); Person.fromJson(Map<String, dynamic> json) : name = json['name'] as String, age = json['age'] as int; @override String toString() => 'Person ($name, $age years old )'; } Future<Iterable<Person>> getPersons() async => HttpClient() .getUrl(Uri.parse(apiUrl)) .then((req) => req.close()) .then((resp) => resp.transform(utf8.decoder).join()) .then((str) => json.decode(str) as List<dynamic>) .then((list) => list.map((e) => Person.fromJson(e))); @immutable abstract class Action { const Action(); } @immutable class LoadPeopleAction extends Action { const LoadPeopleAction(); } @immutable class SuccessfullyFetchedPeopleAction extends Action { final Iterable<Person> persons; const SuccessfullyFetchedPeopleAction({required this.persons}); } @immutable class FailedToFetchPeopleAction extends Action { final Object error; const FailedToFetchPeopleAction({required this.error}); } @immutable class State { final bool isLoading; final Iterable<Person>? fetchedPersons; final Object? error; const State( {required this.isLoading, required this.fetchedPersons, required this.error}); const State.empty() : isLoading = false, fetchedPersons = null, error = null; } State reducer(State oldState, action) { if (action is LoadPeopleAction) { return const State(isLoading: true, fetchedPersons: null, error: null); } else if (action is SuccessfullyFetchedPeopleAction) { return State(isLoading: false, fetchedPersons: action.persons, error: null); } else if (action is FailedToFetchPeopleAction) { return State( isLoading: false, fetchedPersons: oldState.fetchedPersons, error: action.error); } return oldState; } void loadPeopleMiddleware( Store<State> store, dynamic action, NextDispatcher next) { if (action is LoadPeopleAction) { getPersons() .then((persons) => store.dispatch(SuccessfullyFetchedPeopleAction(persons: persons))) .catchError((e) => store.dispatch(FailedToFetchPeopleAction(error: e))); } next(action); } class HomePage extends StatelessWidget { const HomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { final store = Store(reducer, initialState: const State.empty(), middleware: [loadPeopleMiddleware]); return Scaffold( appBar: AppBar( title: const Center( child: Text('Flutter Redux Demo'), ), ), body: StoreProvider( store: store, child: Column(children: [ TextButton( onPressed: () { store.dispatch(const LoadPeopleAction()); }, child: const Center(child: Text('Load Persons'))), StoreConnector<State, bool>( converter: (store) => store.state.isLoading, builder: (context, isLoading) { if (isLoading) { return const CircularProgressIndicator(); } else { return const SizedBox(); } }), StoreConnector<State, Iterable<Person>?>( converter: (store) => store.state.fetchedPersons, builder: (context, people) { if (people == null) { return const SizedBox(); } else { return Expanded( child: ListView.builder( itemCount: people.length, itemBuilder: (context, index) { final person = people.elementAt(index); return ListTile( title: Text(person.name), subtitle: Text('${person.age} years old.'), ); }), ); } }) ]), ), ); } }
people.json文件
[{ "name": "Foo", "age": 20 }, { "name": "Bar", "age": 30 }, { "name": "F", "age": 20 } ]
问题排查与解决方案
1. Store实例重复创建(核心问题)
你在HomePage的build方法中创建Store,每次页面重建(比如旋转屏幕、状态变化)都会生成新的Store实例,导致之前的请求状态丢失。
修复方式:将Store实例提升到应用顶层,确保全局唯一:
修改main和MyApp:
void main() { // 在main中创建Store,确保全局唯一 final store = Store(reducer, initialState: const State.empty(), middleware: [loadPeopleMiddleware]); runApp(MyApp(store: store)); } class MyApp extends StatelessWidget { final Store<State> store; const MyApp({Key? key, required this.store}) : super(key: key); @override Widget build(BuildContext context) { return StoreProvider( store: store, child: MaterialApp( debugShowCheckedModeBanner: false, title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: const HomePage(), ), ); } }
修改HomePage,不再创建Store,通过StoreProvider.of获取:
class HomePage extends StatelessWidget { const HomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Center( child: Text('Flutter Redux Demo'), ), ), body: Column(children: [ TextButton( onPressed: () { // 通过StoreProvider获取全局Store StoreProvider.of<State>(context).dispatch(const LoadPeopleAction()); }, child: const Center(child: Text('Load Persons'))), // 保持原有加载状态组件 StoreConnector<State, bool>( converter: (store) => store.state.isLoading, builder: (context, isLoading) { if (isLoading) { return const CircularProgressIndicator(); } else { return const SizedBox(); } }), // 添加错误信息展示组件 StoreConnector<State, Object?>( converter: (store) => store.state.error, builder: (context, error) { if (error != null) { return Padding( padding: const EdgeInsets.all(8.0), child: Text('加载失败: $error', style: TextStyle(color: Colors.red)), ); } return const SizedBox(); }), // 保持原有列表展示组件 StoreConnector<State, Iterable<Person>?>( converter: (store) => store.state.fetchedPersons, builder: (context, people) { if (people == null) { return const SizedBox(); } else { return Expanded( child: ListView.builder( itemCount: people.length, itemBuilder: (context, index) { final person = people.elementAt(index); return ListTile( title: Text(person.name), subtitle: Text('${person.age} years old.'), ); }), ); } }) ]), ); } }
2. 本地服务器地址适配模拟器
如果使用Android模拟器,127.0.0.1指向模拟器自身,无法访问电脑上的Live Server,需要替换为:
const apiUrl = 'http://10.0.2.2:5500/api/people.json';
iOS模拟器可以直接使用127.0.0.1或localhost。
真机测试时,需要将地址替换为电脑的局域网IP(比如192.168.1.100:5500),确保手机和电脑在同一WiFi下。
3. 验证Live Server可用性
在浏览器中访问http://127.0.0.1:5500/api/people.json,确认能正常返回JSON内容,否则检查Live Server的运行状态和文件路径是否正确。
4. 补充错误展示
添加错误信息展示组件后,可以直接看到请求失败的具体原因(比如网络错误、地址错误),方便进一步排查。
内容的提问来源于stack exchange,提问作者Abdullah Nisar
相关产品推荐
相关产品推荐

