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

Flutter使用SharedPreferences保存字符串列表遇空指针错误及列表不显示问题

问题分析与修复方案

第一个问题:NoSuchMethodError: The method 'add' was called on null

这个错误的核心原因是categoryList被赋值成了null。看你的_update方法:

void _update() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  categoryList = prefs.getStringList('Categories');
}

当你第一次打开应用时,SharedPreferences里还没有保存任何分类数据,prefs.getStringList('Categories')会返回null,这就把原本初始化好的categoryList变成了null。之后调用categoryList.add(category)自然就会触发空指针错误。

第二个问题:ListView初始不显示

你的build方法里虽然写了默认列表的判断,但初始时categoryList是你声明的空列表List<String>(),并不是null,所以那个if (categoryList == null)的逻辑永远不会触发。再加上_update是异步方法,build执行时它还没完成,所以初始UI会显示空的ListView,直到弹窗操作触发setState才会刷新。


具体修复步骤

1. 把初始化逻辑移到initState

build方法会被Flutter频繁调用,不要把异步的_update放在里面,而是放在页面初始化的initState里,只执行一次:

@override
void initState() {
  super.initState();
  _update(); // 页面初始化时加载本地数据
}

2. 修复_update方法,避免categoryList为null

在获取SharedPreferences数据时,用空值合并运算符??给个默认列表,确保categoryList永远不会是null:

void _update() async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  setState(() {
    // 如果本地没有数据,就用默认的['Food', 'Clothes']
    categoryList = prefs.getStringList('Categories') ?? ['Food', 'Clothes'];
  });
}

3. 修改_add和_delete方法,确保UI刷新

修改分类列表后,要调用setState通知Flutter更新UI,同时可以加个小优化:清空输入框、避免添加空字符串:

void _add(BuildContext context, String category) async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  setState(() {
    categoryList.add(category);
    prefs.setStringList('Categories', categoryList);
  });
  _textFieldController.clear(); // 添加后清空输入框
  showSnackBar(context, 'Your category was added');
}

void _delete(BuildContext context, String category) async {
  SharedPreferences prefs = await SharedPreferences.getInstance();
  setState(() {
    categoryList.remove(category);
    prefs.setStringList('Categories', categoryList);
  });
  showSnackBar(context, 'Your category was removed');
}

4. 清理build方法里的冗余代码

删掉build里的这部分代码,因为我们已经在initState和_update里处理了默认值:

if (categoryList == null) {
  setState(() {
    categoryList = ['Food', 'Clothes'];
  });
}
_update();

5. 优化弹窗的添加逻辑

在弹窗的ADD按钮点击时,先判断输入内容不为空再添加,避免无效操作:

FlatButton(
  child: Text('ADD'),
  onPressed: () {
    String name = _textFieldController.text.trim();
    if (name.isNotEmpty) {
      _add(context, name);
    }
    Navigator.of(context).pop();
  },
),

修复后的完整代码

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

class Categories extends StatefulWidget {
  @override
  _CategoriesState createState() => _CategoriesState();
}

class _CategoriesState extends State<Categories> {
  List<String> categoryList = [];
  TextEditingController _textFieldController = TextEditingController();

  @override
  void initState() {
    super.initState();
    _update();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Categories"),
      ),
      body: SafeArea(
        child: Container(
          color: Colors.white,
          child: getCategoriesListView(),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: () {
          _displayDialog(context);
        },
      ),
    );
  }

  ListView getCategoriesListView() {
    return ListView.builder(
        itemCount: categoryList.length,
        itemBuilder: (context, int position) {
          return Card(
            color: Colors.white,
            elevation: 2.0,
            child: ListTile(
              title: Text(categoryList[position]),
              trailing: GestureDetector(
                child: Icon(
                  Icons.delete,
                  color: Colors.grey,
                ),
                onTap: () {
                  _delete(context, categoryList[position]);
                },
              ),
            ),
          );
        });
  }

  void _add(BuildContext context, String category) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    setState(() {
      categoryList.add(category);
      prefs.setStringList('Categories', categoryList);
    });
    _textFieldController.clear();
    showSnackBar(context, 'Your category was added');
  }

  void _delete(BuildContext context, String category) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    setState(() {
      categoryList.remove(category);
      prefs.setStringList('Categories', categoryList);
    });
    showSnackBar(context, 'Your category was removed');
  }

  void _update() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    setState(() {
      categoryList = prefs.getStringList('Categories') ?? ['Food', 'Clothes'];
    });
  }

  void showSnackBar(BuildContext context, String message) async {
    final snackBar = SnackBar(content: Text(message));
    Scaffold.of(context).showSnackBar(snackBar);
  }

  _displayDialog(BuildContext context) async {
    return showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text('Add new category'),
            content: TextField(
              controller: _textFieldController,
            ),
            actions: <Widget>[
              FlatButton(
                child: Text('ADD'),
                onPressed: () {
                  String name = _textFieldController.text.trim();
                  if (name.isNotEmpty) {
                    _add(context, name);
                  }
                  Navigator.of(context).pop();
                },
              ),
              FlatButton(
                child: Text('CANCEL'),
                onPressed: () {
                  Navigator.of(context).pop();
                },
              ),
            ],
          );
        });
  }
}

这样修改后,两个问题都能解决:categoryList永远不会是null,初始打开页面时会从SharedPreferences加载数据(没有的话用默认列表),ListView会正常显示,添加分类也不会报错了。

内容的提问来源于stack exchange,提问作者Frederik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:10:34