In the dynamic realm of Flutter app development, navigation and state management often stand out as vital aspects that shape the user experience. Central to this is the ability to Share Data Between Pages In PageView in Flutter. Understanding this concept not only facilitates smooth navigation between different pages of an app but also ensures consistent data representation throughout. As you embark on this exploration, you’ll uncover the mechanisms behind this essential technique, gaining insights that will significantly elevate your Flutter development journey.
PageView is a scrollable list that works page by page. Think of it
like a carousel or a book, where each swipe takes you to a new page,
presenting content in a discrete manner. Unlike traditional lists that
scroll vertically item by item, PageView gives developers the
opportunity to create horizontal swipes, each revealing a new “page” of
content. This has opened up a new avenue for onboarding flows, content
carousels, or simple tabbed navigation where each tab is essentially a
page.
Understanding PageView
At its core, PageView requires only a list of child widgets, each
representing a page. Its structure is somewhat reminiscent of Flutter’s
ListView widget. However, rather than vertically scrolling through
items, users horizontally navigate through pages. You can control its
initial page, the direction of scrolling (vertical is possible too), or
even the physics of the scroll, among other properties.
PageView(
children: <Widget>[
Container(color: Colors.red),
Container(color: Colors.green),
Container(color: Colors.blue),
],
)
With just these few lines, we have a simple horizontal swiper with three colored pages.
The Challenge of Data Sharing in PageView
When building more complex applications, it’s common to require data to
be accessible across multiple pages within a PageView. For instance,
consider an onboarding flow where a user’s choices on one page determine
the content they see on subsequent pages. Achieving this seamless data
flow can pose challenges.
The PageView itself doesn’t inherently provide mechanisms for pages to
share data. Each child page is essentially a distinct widget. Hence, the
challenge: How do you ensure that data changes or selections in one page
can be instantly reflected, accessed, or even modified in another?
In the subsequent sections, we will delve into the strategies that allow for effective data sharing between pages, ensuring a coherent and responsive user experience.
Simple Data Sharing: Using Parent State
When working with Flutter, sharing data between widgets or pages often
begins with the parent-child relationship. One straightforward way to
share data between pages inside a PageView is to manage the state at
the parent level. By creating a shared state in the parent widget and
passing it down to the child pages, any changes made to this state can
be reflected across all pages.
Methods to Share Data between Pages in PageView
1. Using a Common Parent State
1.1 Creating a Shared State in the Parent Widget
To understand how to share data between pages using the parent state,
let’s consider a simple example where we have a PageView with two
pages. These pages will display a counter value, and each page will have
a button to increment this counter.
First, let’s define our shared state in the parent widget:
class MainPage extends StatefulWidget {
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return PageView(
children: [
Page1(counter: _counter, incrementCounter: _incrementCounter),
Page2(counter: _counter, incrementCounter: _incrementCounter),
],
);
}
}
Here, the _counter is the shared state, and the _incrementCounter
method updates this state.
1.2 Passing and Updating Data in Child Pages
Now, let’s define our child pages and see how to share data between pages:
class Page1 extends StatelessWidget {
final int counter;
final Function incrementCounter;
Page1({required this.counter, required this.incrementCounter});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Counter Value: $counter'),
ElevatedButton(
onPressed: () => incrementCounter(),
child: Text('Increment'),
),
],
),
),
);
}
}
class Page2 extends StatelessWidget {
final int counter;
final Function incrementCounter;
Page2({required this.counter, required this.incrementCounter});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Counter Value: $counter'),
ElevatedButton(
onPressed: () => incrementCounter(),
child: Text('Increment'),
),
],
),
),
);
}
}
In both Page1 and Page2, we are passing the counter value and the
incrementCounter method as parameters. This allows us to display the
shared counter value on each page and increment it via a button.
2. Using Provider or another State Management Solution
The Provider package is a state management tool designed to simplify
data flow in your app and reduce boilerplate code. It allows widgets to
subscribe to changes in the state and rebuild when necessary. In
essence, the Provider package provides a way to “inject” data where
it’s needed, ensuring that widgets can access shared data without having
to pass it down through every level of the widget tree.
2.1 Setting up a ChangeNotifier for Shared Data
To share data between pages using the Provider, we’ll use the
ChangeNotifier class. The ChangeNotifier allows you to notify
listeners (in our case, widgets) when a change occurs. Let’s look at how
to set it up:
First, we need to define our shared data class:
class CounterModel extends ChangeNotifier {
int _counter = 0;
int get counter => _counter;
void increment() {
_counter++;
notifyListeners(); // This will alert the widgets that are listening to this model.
}
}
Next, we’ll wrap our app (or part of it where PageView resides) in a
ChangeNotifierProvider:
ChangeNotifierProvider(
create: (context) => CounterModel(),
child: YourApp(),
)
2.2 Accessing and Modifying Shared Data in Pages using context.watch
and context.read
With our Provider set up, we can now access and modify our shared data
within the pages.
To access the data:
Use context.watch<T>() when the widget needs to rebuild when the model
changes.
final counterModel = context.watch<CounterModel>();
To modify the data without rebuilding the widget:
Use context.read<T>().
final counterModel = context.read<CounterModel>();
counterModel.increment();
For our PageView example, the pages will look something like this:
class Page1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counterModel = context.watch<CounterModel>();
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Counter Value: ${counterModel.counter}'),
ElevatedButton(
onPressed: () => context.read<CounterModel>().increment(),
child: Text('Increment'),
),
],
),
),
);
}
}
The Page2 will have a similar structure, allowing it to share data
between pages using the Provider package seamlessly.
Practical Example: Sharing Data Between Two Pages in a PageView
Having understood the theoretical aspect of how to share data between
pages using the Provider package, let’s dive into a hands-on example.
We will walk through creating a simple Flutter app with two pages in a
PageView, both of which will share data.
1. Setting up the Application:
Start by creating a new Flutter project:
flutter create shared_data_app
Navigate to the project directory:
cd shared_data_app
Now, add the Provider package to your pubspec.yaml under
dependencies:
dependencies:
flutter:
sdk: flutter
provider: ^latest_version
Run flutter pub get to install the package.
2. Create the Data Model with ChangeNotifier:
In the lib directory, create a new file named counter_model.dart. In
this file, define your data model:
import 'package:flutter/foundation.dart';
class CounterModel extends ChangeNotifier {
int _counter = 0;
int get counter => _counter;
void increment() {
_counter++;
notifyListeners();
}
}
3. Set up the Main App with Provider:
Now, in your main.dart, set up the main app:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter_model.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => CounterModel(),
child: MaterialApp(
home: HomeScreen(),
),
);
}
}
4. Design the HomeScreen with PageView:
Create the HomeScreen widget that contains the PageView:
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
children: [
CounterPage(title: "Page 1"),
CounterPage(title: "Page 2"),
],
),
);
}
}
5. Design the CounterPage:
Now, let’s define the CounterPage which will share data between pages:
class CounterPage extends StatelessWidget {
final String title;
CounterPage({required this.title});
@override
Widget build(BuildContext context) {
final counterModel = context.watch<CounterModel>();
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Counter Value: ${counterModel.counter}'),
ElevatedButton(
onPressed: () => context.read<CounterModel>().increment(),
child: Text('Increment'),
),
],
),
),
);
}
}
Now, when you run the app, you’ll see a PageView with two pages: “Page
1” and “Page 2”. Both pages share the same counter value. When you
increment the counter on one page, navigate to the other, you’ll notice
that the counter value is updated there as well, demonstrating how you
can effortlessly share data between pages using the Provider package
in Flutter.
Let’s Wrap Up
In summary, mastering how to effectively share data between pages within
a PageView in Flutter is pivotal for creating smooth, scalable, and
efficient applications. It streamlines the data flow, enhances the user
experience, and reduces the complexity of the codebase. The Provider
package emerges as a powerful tool in achieving this, providing a robust
and clean architecture for managing state and data sharing across
various sections of your Flutter applications.
Further Reading
For additional insights and understanding, make sure to visit the following links from the official Flutter documentation:


