You need to enable JavaScript to run this app. Flutter集成Provider后电影列表不显示、搜索时报results为null的问题排查
优惠活动
大模型
产品
解决方案
定价
更多

Flutter集成Provider后电影列表不显示、搜索时报results为null的问题排查

问题根因定位

所有异常均为代码逻辑疏漏导致,和Provider的配置、网络请求能力无关,核心问题共4处:

  • 状态类拉取数据后未赋值内部私有字段:MovieInfo中的fetchData方法解析完接口返回值后,仅创建了局部变量存储结果、直接返回,全程未给类内的_result字段赋值,导致对外暴露的result属性永远返回null。
  • 变量作用域遮蔽导致空引用:_HomeState中声明的成员变量movieInfo从未被实际赋值,build方法里重新以同名类型声明了局部变量MovieInfo movieInfo = Provider.of...,把外层的成员变量完全遮蔽;后续在_buildShow这类类内方法中读到的movieInfo实际是未初始化的null,访问属性自然触发空指针报错。
  • 空值校验缺失:搜索框的onChanged回调、列表构建逻辑中,直接对可能为null的result读取.results属性,一旦接口还没返回结果就触发输入搜索,直接抛空异常。
  • 资源未正确释放:MovieInfo中持有的TextEditingController没有随组件生命周期执行dispose,长期运行会引发内存泄漏警告。
分步修复方案

1. 修正状态类的赋值逻辑

删除fetchData中的局部变量,将解析结果直接赋值给私有字段,同时补上资源释放逻辑:

class MovieInfo extends ChangeNotifier {
  Movies _result;
  Movies get result => _result;

  final myController = TextEditingController();
  final List<Results> filteredItems = [];

  Future<void> fetchData() async {
    var url = Uri.parse(
        'https://api.themoviedb.org/3/movie/upcoming?api_key=你的合法密钥&language=ko-KR&page=1');
    var response = await http.get(url);
    // 直接将解析结果赋值给内部私有字段
    _result = Movies.fromJson(json.decode(response.body));
    notifyListeners();
  }

  // 重写dispose释放控制器资源
  @override
  void dispose() {
    myController.dispose();
    super.dispose();
  }
}

2. 解决变量遮蔽问题

删掉build方法里的同名局部变量,将Provider获取到的状态实例赋值给类的成员变量,保证类内所有方法都能拿到正确的状态引用:

class _HomeState extends State<Home> {
  late MovieInfo movieInfo;

  @override
  void initState() {
    super.initState();
    // listen: false的写法符合Provider规范,无需迁移到didChangeDependencies
    Provider.of<MovieInfo>(context, listen: false).fetchData();
  }

  @override
  Widget build(BuildContext context) {
    // 给成员变量赋值,不要重新声明同类型的局部变量
    movieInfo = Provider.of<MovieInfo>(context);
    final result = movieInfo.result;

    return Scaffold(
      // 原有drawer、appBar逻辑保持不变
      body: Scaffold(
        backgroundColor: Colors.blueGrey,
        body: Column(
          children: <Widget>[
            TextField(
              controller: movieInfo.myController,
              onChanged: (text) {
                // 未加载完成时直接跳过搜索逻辑,避免空指针
                if (result == null) return;
                setState(() {
                  movieInfo.filteredItems.clear();
                  movieInfo.filteredItems.addAll(result.results
                      .where((item) => item.title.contains(text)));
                });
              },
              // 原有输入框装饰逻辑保持不变
            ),
            Expanded(
              child: GridView.count(
                crossAxisCount: 3,
                childAspectRatio: 2 / 3.5,
                // 加载中展示提示,替代原来的空列表
                children: result == null
                    ? [Center(child: CircularProgressIndicator())]
                    : _buildShow(),
              ),
            ),
          ],
        ),
      ),
    );
  }

  // _buildItem方法保持原有逻辑不变
  Widget _buildItem(Results movies) {
    return InkWell(
      child: Card(
        color: Colors.blueGrey,
        margin: EdgeInsets.symmetric(vertical: 5.0, horizontal: 10.0),
        child: Column(
          children: [
            Image.network('https://image.tmdb.org/t/p/w500/${movies.posterPath}'),
            Text('${movies.title}',
                style: TextStyle(
                    height: 1.5, fontSize: 15, fontWeight: FontWeight.bold)),
          ],
        ),
      ),
      onTap: () {
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (context) => DetailScreen(movie: movies),
          ),
        );
      },
    );
  }

  // 给列表构建入口补空值校验
  List<Widget> _buildShow() {
    if (movieInfo.result == null) return [];
    if (movieInfo.myController.text.isEmpty) {
      return movieInfo.result.results.map((movies) => _buildItem(movies)).toList();
    }
    return movieInfo.filteredItems.map((e) => _buildItem(e)).toList();
  }
}
补充注意事项
  • 原代码中TMDB接口的api_key为空,需要替换为合法的访问密钥,否则即使代码逻辑正确,也会拿到接口错误响应,无法解析出有效数据
  • 原State类中声明的Future<Movies> data;字段从未被使用,属于冗余代码,可以直接删除减少无效内存占用
  • 列表加载初期展示加载指示器,比直接渲染空列表的用户体验更好,也能避免用户误以为页面加载失败

内容的提问来源于stack exchange,提问作者jichang brad Jung

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.23 14:15:07