0

Scenario: I have a page with a ListView which shows a list of several items from the database, fetched using a REST API. I am loading the data in the initState() method. In order to load the data, I also need some data stored in the Shared Preferences. I obtain the instance of shared preferences before loading the data and rendering the UI. The UI changes based on the AccountType stored in the Shared Preferences.
Problem: When I navigate to the specific page in the app, I get an error-

LateInitializationError: Field '_sharedPreferences@53458703' has not been initialized.
The relevant error-causing widget was: 
  QuizListPage QuizListPage:file:///D:/Github_Work/Assessment%20Portal/assessmentportal/lib/Pages/CategoryTile.dart:27:41
When the exception was thrown, this was the stack: 
#0      _QuizListPageState._sharedPreferences (package:assessmentportal/Pages/QuizListPage.dart)
#1      _QuizListPageState.build (package:assessmentportal/Pages/QuizListPage.dart:72:19)
#2      StatefulElement.build (package:flutter/src/widgets/framework.dart:4992:27)
#3      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4878:15)
#4      StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5050:11)
#5      Element.rebuild (package:flutter/src/widgets/framework.dart:4604:5)
#6      ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:4859:5)
#7      StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5041:11)
#8      ComponentElement.mount (package:flutter/src/widgets/framework.dart:4853:5)
...     Normal element mounting (275 frames)

My code is-

class QuizListPage extends StatefulWidget {
  CategoryModel category;

  QuizListPage({required this.category});

  @override
  State<QuizListPage> createState() => _QuizListPageState();
}

class _QuizListPageState extends State<QuizListPage> {
  late SharedPreferences _sharedPreferences;
  List<QuizModel> quizzes = [];
  late CategoryService _categoryService;
  bool _areQuizzesLoaded = false;
  @override
  void initState() {
    super.initState();

    _initializeData();
  }

  void _initializeData() async {
    _sharedPreferences = await SharedPreferences.getInstance();
    _categoryService = CategoryService();
    setState(() {
      _areQuizzesLoaded = false;
    });
    await _loadCategories();
  }

  Future<void> _loadCategories() async {
    if (_sharedPreferences.getString(ROLE) == ROLE_NORMAL) {
      quizzes = await _categoryService.getAllQuizzesByCategory(
          _sharedPreferences.getString(BEARER_TOKEN) ?? 'null',
          widget.category.categoryId);
    } else {
      quizzes = await _categoryService.getAllQuizzesByAdminAndCategory(
        adminid: _sharedPreferences.getInt(USER_ID) ?? 0,
        categoryid: widget.category.categoryId,
        token: _sharedPreferences.getString(BEARER_TOKEN) ?? 'null',
      );
    }
    setState(() {
      log('Setting loaded to true');
      _areQuizzesLoaded = true;
    });
  }

  @override
  Widget build(BuildContext context) {
    final height = MediaQuery.of(context).size.height -
        AppBar().preferredSize.height -
        MediaQuery.of(context).padding.top -
        MediaQuery.of(context).padding.bottom;
    final width = MediaQuery.of(context).size.width;
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.category.categoryTitle),
        actions: (_sharedPreferences.getString(ROLE) != ROLE_NORMAL)
            ? [
                Container(
                  margin: const EdgeInsets.only(
                    top: 10,
                    right: 10,
                    left: 5,
                    bottom: 5,
                  ),
                  child: ElevatedButton(
                    onPressed: () {
                      Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) => AddQuizPage(
                            userid: _sharedPreferences.getInt(USER_ID) ?? 0,
                            categoryid: widget.category.categoryId ?? 0,
                            token: _sharedPreferences.getString(BEARER_TOKEN) ??
                                'null',
                          ),
                        ),
                      );
                    },
                    child: const Text('Add Quiz'),
                  ),
                ),
              ]
            : null,
      ),
      body: Container(
        child:Text('Hello'),
           ),
         );
        }
   }

Please help me understand why this error occurs (sometimes not always) and what is the correct way to load data before showing the UI (Some UI components depend on the data stored in shared preferences), I want to use Shared Preferences, the way its used in my code. Please help!

ayush
  • 464
  • 5
  • 17
  • You could use `Future` in combination with `FutureBuilder`. Have a look at [https://pub.dev/packages/shared_preferences/example](https://pub.dev/packages/shared_preferences/example) – abichinger Nov 24 '22 at 18:15

1 Answers1

0

You can try to modify it to the following code:

bool _spInitOK = false;

void _initializeData() async {
  _sharedPreferences = await SharedPreferences.getInstance();
  setState(() {
    _spInitOK = true;
  });
  ...
}

actions: !_spInitOK
    ? null
    : (_sharedPreferences.getString(ROLE) != ROLE_NORMAL)
        ? [...

The following is my understanding:

@override
void initState() {
  super.initState();
  debugPrint("[${DateTime.now()}] _initializeData before");
  _initializeData();
  debugPrint("[${DateTime.now()}] _initializeData after");
}

void _initializeData() async {
  debugPrint("[${DateTime.now()}] _initializeData start");
  await Future.delayed(Duration(seconds: 2)); // await 2s
  _sharedPreferences = await SharedPreferences.getInstance();
  setState(() {
    _spInitOK = true;
  });
  ...
  debugPrint("[${DateTime.now()}] _initializeData end");
}

@override
Widget build(BuildContext context) {
  debugPrint("[${DateTime.now()}] build");
  ...
}
I/flutter (26518): [2022-11-25 01:03:52.868143] _initializeData before
I/flutter (26518): [2022-11-25 01:03:52.878696] _initializeData start
I/flutter (26518): [2022-11-25 01:03:52.881150] _initializeData after
I/flutter (26518): [2022-11-25 01:03:52.887225] build // first build, _sharedPreferences is null in this moment, so can't use it
// 2s
I/flutter (26518): [2022-11-25 01:03:54.896708] _initializeData end
I/flutter (26518): [2022-11-25 01:03:54.904256] build // second build
linyi102
  • 1
  • 1