Skip to content

Immutability

In Disco, providers are immutable. This means that once a provider is created, it cannot be changed.

This is a design choice that we made to ensure that the instance of the provider is consistent throughout the application.

Let’s look at an example, think a bit about the output after the FloatingActionButton is pressed, then continue reading to see if you were correct.

import 'package:disco/disco.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MainApp());
}
final counterProvider =
Provider.withArgument((context, Counter counter) => counter);
class Counter {
Counter(this.value);
final int value;
}
class MainApp extends StatefulWidget {
const MainApp({super.key});
@override
State<MainApp> createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
var counter = Counter(0);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Immutability',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ProviderScope(
providers: [
counterProvider(counter),
],
child: Scaffold(
body: Builder(
builder: (context) {
return Center(
child: Text('The counter is ${counterProvider.of(context).value}'),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
counter = Counter(counter.value + 1);
});
},
child: const Icon(Icons.add),
),
),
),
);
}
}

The example renders The counter is 0. After the user presses the FloatingActionButton, the counter is incremented, but the UI does not update. This is because the provider is immutable, and the instance of the provider is not updated. In other words, the counterProvider is created only the first time and it still provides the initial instance.

You may be skeptical about the immutability of providers, but it is actually a good thing. It helps avoid bugs and makes the application more predictable.

The typical usage for a provider is to provide a value for a specific page or widget. When the page or widget is disposed, the provider gets also disposed, and the value is no longer available. When the Page is opened again, a new instance of the provider is created, and the value is provided again.

Force the recreation of a provider

There is one way to force the recreation of a provider, which we discourage, but can be used if you know what you are doing.

ProviderScope(
key: ValueKey(counter),
providers: [
counterProvider(counter),
],
child: ..
)

When changing a key, the widget subtree gets regenerated. The provider recreation is performed inside the ProviderScope widget. What exactly happens is the following:

  1. The old ProviderScope is disposed, which disposes of all the providers inside it.
  2. A new ProviderScope is created and all the providers are created again.