How to save an index of PageView and load it when reopening the app?

Issue

I want to save an index of PageView and load it when reopening the app.
For example, if a user closes the app on page 2, the app should be started on page 2.

I tried to implement this feature by using shared_preferences and provider.
However, the app always starts on page 1, probably because PageController‘s initialPage is initialized as 0. BottomNavigationbar‘s BottomNavigationBarItem works as expected.
How can I save an index of PageView and load it when a user reopens the app?

main.dart

import 'package:flutter/material.dart';
import 'package:test/home_screen.dart';
import 'package:test/provider_data.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => ProviderData(),
      child: MaterialApp(
        title: 'test',
        home: const HomeScreen(),
      ),
    );
  }
}

home_screen.dart

import 'package:flutter/material.dart';
import 'package:test/page1.dart';
import 'package:test/page2.dart';
import 'package:test/provider_data.dart';
import 'package:provider/provider.dart';

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  final List<Widget> _pageList = const [
    Page1(),
    Page2(),
  ];

  final PageController _pageController = PageController();

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Consumer<ProviderData>(
          builder: (context, providerData, child) {
            return PageView(
              controller: _pageController,
              children: _pageList,
              onPageChanged: (index) {
                providerData.changePageIndex(index);
              },
            );
          },
        ),
      ),
      bottomNavigationBar: Consumer<ProviderData>(
        builder: (context, providerData, child) {
          return BottomNavigationBar(
            onTap: (index) {
              _pageController.animateToPage(
                index,
                duration: const Duration(microseconds: 10),
                curve: Curves.linear,
              );
              providerData.changePageIndex(index);
            },
            type: BottomNavigationBarType.fixed,
            currentIndex: providerData.pageIndex!,
            items: const [
              BottomNavigationBarItem(
                icon: Icon(Icons.search),
                label: 'Page 1',
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.home),
                label: 'Page 2',
              ),
            ],
          );
        },
      ),
    );
  }
}

provider_data.dart

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

class ProviderData extends ChangeNotifier {
  SharedPreferences? prefs;
  final String pageIndexKey = 'pageIndex';
  int? _pageIndex;
  int? get pageIndex => _pageIndex;

  ProviderData() {
    _pageIndex = 0;
    _loadFromPrefs();
  }

  _initPrefs() async {
    prefs ??= await SharedPreferences.getInstance();
  }

  _loadFromPrefs() async {
    await _initPrefs();
    _pageIndex = prefs!.getInt(pageIndexKey) ?? 0;
    notifyListeners();
  }

  _savePageIndexToPrefs({required String key, required int value}) async {
    await _initPrefs();
    prefs!.setInt(key, value);
  }

  void changePageIndex(int newPageIndex) {
    _pageIndex = newPageIndex;
    _savePageIndexToPrefs(key: pageIndexKey, value: _pageIndex!);
    notifyListeners();
  }
}

Solution

Retrieve the index of the page view in initState()

Firstly, you should change _loadFromPrefs() method to be public then call this method in initState() method and assign the returning value into the PageContoller(initialPage: value) with the following form:

in home_screen.dart

  late PageController _pageController;

  @override
  void initState() {
    super.initState();
    ProviderData().loadFromPrefs().then((value) {
      _pageController = PageController(initialPage: value);
    });
  }

in provider_data.dart

  loadFromPrefs() async {
    await _initPrefs();
    _pageIndex = prefs!.getInt(pageIndexKey) ?? 0;
    notifyListeners();
  }

Answered By – Ahmed Khairy

Answer Checked By – Marilyn (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *