Skip to content

ReactterProvider

The ReactterProvider widget injects a dependency into its descendant widgets, ensuring the dependency is accessible throughout its subtree. It facilitates sharing the dependency across multiple widgets seamlessly.

Syntax

The ReactterProvider class has three constructors, each catering to different dependency injection needs.

  • ReactterProvider : Default constructor that provides a dependency using a builder function.

    ReactterProvider<T>(
    T instanceBuilder(), {
    Key? key,
    String? id,
    DependencyMode mode = DependencyMode.builder,
    Widget? child,
    Widget builder(
    BuildContext context,
    T instance,
    Widget? child,
    )?,
    })
  • ReactterProvider.init : This constructor initializes the dependency instance before it is mounted.

    ReactterProvider<T>.init(
    T instanceBuilder(), {
    Key? key,
    String? id,
    DependencyMode mode = DependencyMode.builder,
    Widget? child,
    Widget builder(
    BuildContext context,
    T instance,
    Widget? child,
    )?,
    })
  • ReactterProvider.lazy : This constructor defers the instantiation of the dependency until it is accessed for the first time.

    ReactterProvider<T>.lazy(
    T instanceBuilder(), {
    Key? key,
    String? id,
    DependencyMode mode = DependencyMode.builder,
    Widget? child,
    Widget builder(
    BuildContext context,
    Widget? child,
    )?,
    })

Properties

  • instanceBuilder : A function that creates an instance of the T dependency. This function is registered by Dependency Injection.
  • key: An optional Key to use for identifying the widget.
  • id: An optional identifier for the dependency. If omitted, the dependency will be located by its type( T ).
  • mode: The mode of the dependency injection. By default, it uses the builder mode(DependecyMode.builder). Learn more about the dependency modes.
  • child: An optional Widget which is independent of the ReactterProvider . If defined, it is passed to the builder function if it is defined.
  • builder : An optional function which builds a widget based on the dependency. If it not defined, the child widget is returned. It receives the following arguments:
    • context: The BuildContext of the widget. A handle to the location of ReactterProvider in the widget tree.
    • instance: The instance of the T dependency. It is available only to default constructor.
    • child: The child widget passed to the ReactterProviders widget.

Usage

Basic Usage

To use the ReactterProvider widget, wrap it around the widget tree where you want to inject the dependency.

Here’s an example of how to use it:

1
import 'package:flutter/material.dart';
2
import 'package:flutter_reactter/flutter_reactter.dart';
3
import 'counter_controller.dart';
4
5
class Counter extends StatelessWidget {
6
const Counter({Key? key}) : super(key: key);
7
8
@override
9
Widget build(BuildContext context) {
10
// Provides the `CounterController` dependency to the widget tree
11
return ReactterProvider<CounterController>(
12
() => CounterController(),
13
builder: (context, counterController, child) {
23 collapsed lines
14
return Row(
15
mainAxisSize: MainAxisSize.min,
16
children: [
17
ElevatedButton(
18
onPressed: counterController.decrement,
19
child: const Icon(Icons.remove),
20
),
21
const SizedBox(width: 8),
22
// Observes the `count` property of the `counterController`
23
// and rebuilds the widget tree when the `count` value changes
24
ReactterConsumer<CounterController>(
25
listenStates: (counterController) => [counterController.count],
26
builder: (context, counterController, child) {
27
return Text("${counterController.count}");
28
},
29
),
30
const SizedBox(width: 8),
31
ElevatedButton(
32
onPressed: counterController.increment,
33
child: const Icon(Icons.add),
34
),
35
],
36
);
37
},
38
);
39
}
40
}
1
import 'package:reactter/reactter.dart';
2
3
class CounterController {
4
// Create a reactive state using the `Signal` class
5
final count = Signal(0);
6
7
void increment() {
8
count.value++;
9
}
10
11
void decrement() {
12
count.value--;
13
}
14
}
1
import 'package:flutter/material.dart';
2
import 'counter.dart';
3
4
class CounterView extends StatelessWidget {
5
const CounterView({Key? key}) : super(key: key);
6
7
@override
8
Widget build(BuildContext context) {
9
return Scaffold(
10
appBar: AppBar(
11
title: const Text("Counter"),
12
),
13
body: const Center(
14
child: Counter(),
15
),
16
);
17
}
18
}
1
import 'package:flutter/material.dart';
2
import 'counter_view.dart';
3
4
void main() {
5
runApp(MyApp());
6
}
7
8
class MyApp extends StatelessWidget {
9
@override
10
Widget build(BuildContext context) {
11
return MaterialApp(
12
home: CounterView(),
13
);
14
}
15
}

Using id

The ReactterProvider widget provides an id argument that allows you to provide an identifier for the dependency. It is useful when you want to create multiple instances of the same dependency.

Here’s an example of how to use it:

1
import 'package:flutter/material.dart';
2
import 'package:flutter_reactter/flutter_reactter.dart';
3
import 'counter_controller.dart';
4
5
class Counter extends StatelessWidget {
6
final String? id;
7
8
const Counter({Key? key, this.id}) : super(key: key);
9
10
@override
11
Widget build(BuildContext context) {
12
// Provides the `CounterController` dependency to the widget tree
13
return ReactterProvider<CounterController>(
14
() => CounterController(),
15
id: id,
16
builder: (context, counterController, child) {
24 collapsed lines
17
return Row(
18
mainAxisSize: MainAxisSize.min,
19
children: [
20
ElevatedButton(
21
onPressed: counterController.decrement,
22
child: const Icon(Icons.remove),
23
),
24
const SizedBox(width: 8),
25
// Observes the `count` property of the `counterController`
26
// and rebuilds the widget tree when the `count` value changes
27
ReactterConsumer<CounterController>(
28
id: id,
29
listenStates: (counterController) => [counterController.count],
30
builder: (context, counterController, child) {
31
return Text("${counterController.count}");
32
},
33
),
34
const SizedBox(width: 8),
35
ElevatedButton(
36
onPressed: counterController.increment,
37
child: const Icon(Icons.add),
38
),
39
],
40
);
41
},
42
);
43
}
44
}
1
import 'package:flutter/material.dart';
2
import 'counter.dart';
3
4
class CounterView extends StatelessWidget {
5
const CounterView({Key? key}) : super(key: key);
6
7
@override
8
Widget build(BuildContext context) {
9
return Scaffold(
10
appBar: AppBar(
11
title: const Text("Counter"),
12
),
13
body: Center(
14
child: Column(
15
mainAxisAlignment: MainAxisAlignment.center,
16
children: const [
17
Counter(id: 'counter1'),
18
SizedBox(height: 8),
19
Counter(id: 'counter2'),
20
SizedBox(height: 8),
21
Counter(id: 'counter1'),
22
],
23
),
24
),
25
);
26
}
27
}
1
import 'package:reactter/reactter.dart';
2
3
class CounterController {
4
// Create a reactive state using the `Signal` class
5
final count = Signal(0);
6
7
void increment() {
8
count.value++;
9
}
10
11
void decrement() {
12
count.value--;
13
}
14
}
1
import 'package:flutter/material.dart';
2
import 'counter_view.dart';
3
4
void main() {
5
runApp(MyApp());
6
}
7
8
class MyApp extends StatelessWidget {
9
@override
10
Widget build(BuildContext context) {
11
return MaterialApp(
12
home: CounterView(),
13
);
14
}
15
}

Using child

The ReactterProvider widget provides a child argument that does not rebuild when the dependency changes. It is useful when you want to reuse a widget.

Here’s an example of how to use it:

1
import 'package:flutter/material.dart';
2
import 'package:flutter_reactter/flutter_reactter.dart';
3
import 'counter_controller.dart';
4
5
class Counter extends StatelessWidget {
6
final InstanceChildBuilder<CounterController>? builder;
7
8
const Counter({Key? key, this.builder}) : super(key: key);
9
10
Widget build(BuildContext context) {
11
// Provides the `CounterController` dependency to the widget tree
12
return ReactterProvider<CounterController>(
13
() => CounterController(), // Registers the `CounterController` dependency
14
child: ReactterConsumer<CounterController>(
15
// Observes the `count` property of the `counterController` instance
16
listenStates: (counterController) => [counterController.count],
17
builder: (context, counterController, child) {
18
// Rebuilds the widget tree when the `count` value changes
19
return Text("${counterController.count}");
20
},
21
),
22
builder: builder,
23
);
24
}
25
}
1
import 'package:flutter/material.dart';
2
import 'counter.dart';
3
4
class CounterWithButtons extends StatelessWidget {
5
const CounterWithButtons({Key? key}) : super(key: key);
6
7
Widget build(BuildContext context) {
8
return Counter(
9
builder: (context, counterController, child) {
10
return Column(
11
mainAxisAlignment: MainAxisAlignment.center,
12
children: [
13
Row(
14
mainAxisAlignment: MainAxisAlignment.center,
15
children: [
16
ElevatedButton(
17
onPressed: counterController.decrement,
18
child: const Icon(Icons.remove),
19
),
20
const SizedBox(width: 8),
21
if (child != null) child,
22
const SizedBox(width: 8),
23
ElevatedButton(
24
onPressed: counterController.increment,
25
child: const Icon(Icons.add),
26
),
27
],
28
),
29
],
30
);
31
},
32
);
33
}
34
}
1
import 'package:flutter/material.dart';
2
import 'counter.dart';
3
import 'counter_with_buttons.dart';
4
5
class CounterView extends StatelessWidget {
6
const CounterView({Key? key}) : super(key: key);
7
8
@override
9
Widget build(BuildContext context) {
10
return Scaffold(
11
appBar: AppBar(
12
title: const Text('Counter'),
13
),
14
body: const Center(
15
child: CounterWithButtons(),
16
),
17
floatingActionButton: const CircleAvatar(
18
child: Counter(),
19
),
20
);
21
}
22
}
1
import 'package:reactter/reactter.dart';
2
3
class CounterController {
4
// Create a reactive state using the `Signal` class
5
final count = Signal(0);
6
7
void increment() {
8
count.value++;
9
}
10
11
void decrement() {
12
count.value--;
13
}
14
}
1
import 'package:flutter/material.dart';
2
import 'counter_view.dart';
3
4
void main() {
5
runApp(MyApp());
6
}
7
8
class MyApp extends StatelessWidget {
9
@override
10
Widget build(BuildContext context) {
11
return MaterialApp(
12
home: CounterView(),
13
);
14
}
15
}

Immediate initialization

The ReactterProvider.init constructor initializes the dependency before it is mounted. It is useful when you want to initialize the dependency immediately.

Here’s an example of how to use it:

1
import 'package:flutter/material.dart';
2
import 'package:flutter_reactter/flutter_reactter.dart';
3
import 'counter_controller.dart';
4
5
class Counter extends StatelessWidget {
6
const Counter({Key? key}) : super(key: key);
7
8
@override
9
Widget build(BuildContext context) {
10
// Immediately initializes the `CounterController` dependency for providing it to the widget tree
11
final widgetToRender = ReactterProvider<CounterController>.init(
12
() {
13
print('CounterController created');
14
return CounterController();
15
},
16
builder: (context, counterController, child) {
23 collapsed lines
17
return Row(
18
mainAxisSize: MainAxisSize.min,
19
children: [
20
ElevatedButton(
21
onPressed: counterController.decrement,
22
child: const Icon(Icons.remove),
23
),
24
const SizedBox(width: 8),
25
// Observes the `count` property of the `counterController`
26
// and rebuilds the widget tree when the `count` value changes
27
ReactterConsumer<CounterController>(
28
listenStates: (counterController) => [counterController.count],
29
builder: (context, counterController, child) {
30
return Text("${counterController.count}");
31
},
32
),
33
const SizedBox(width: 8),
34
ElevatedButton(
35
onPressed: counterController.increment,
36
child: const Icon(Icons.add),
37
),
38
],
39
);
40
},
41
);
42
43
// The `CounterController` instance can be use here
44
// to call methods or access properties
45
Reactter.get<CounterController>()?.count.value = 10;
46
47
// A render delay of 10 seconds
48
return FutureBuilder(
49
future: Future.delayed(
50
const Duration(seconds: 10),
51
() => widgetToRender,
52
),
53
builder: (context, snapshot) {
54
if (!snapshot.hasData) {
55
return const Center(
56
child: CircularProgressIndicator(),
57
);
58
}
59
60
return snapshot.data as Widget;
61
});
62
}
63
}
1
import 'package:reactter/reactter.dart';
2
3
class CounterController {
4
// Create a reactive state using the `Signal` class
5
final count = Signal(0);
6
7
void increment() {
8
count.value++;
9
}
10
11
void decrement() {
12
count.value--;
13
}
14
}
1
import 'package:flutter/material.dart';
2
import 'counter.dart';
3
4
class CounterView extends StatelessWidget {
5
const CounterView({Key? key}) : super(key: key);
6
7
@override
8
Widget build(BuildContext context) {
9
return Scaffold(
10
appBar: AppBar(
11
title: const Text('Counter'),
12
),
13
body: const Center(
14
child: Counter(),
15
),
16
);
17
}
18
}
1
import 'package:flutter/material.dart';
2
import 'counter_view.dart';
3
4
void main() {
5
runApp(MyApp());
6
}
7
8
class MyApp extends StatelessWidget {
9
@override
10
Widget build(BuildContext context) {
11
return MaterialApp(
12
home: CounterView(),
13
);
14
}
15
}

Initialization lazily

The ReactterProvider.lazy constructor initializes the dependency lazily. It creates the dependency only when it is accessed for the first time.

Here’s an example of how to use it:

1
import 'package:flutter/material.dart';
2
import 'package:flutter_reactter/flutter_reactter.dart';
3
import 'counter_controller.dart';
4
5
class Counter extends StatelessWidget {
6
const Counter({Key? key}) : super(key: key);
7
8
@override
9
Widget build(BuildContext context) {
10
// Provides the `CounterController` dependency to the widget tree
11
return ReactterProvider<CounterController>.lazy(
12
() {
13
print('CounterController created');
14
return CounterController();
15
},
16
builder: (context, child) {
17
final showCounter = Signal(false);
18
19
return ReactterWatcher(
20
builder: (context, child) {
21
if (!showCounter.value) {
22
return ElevatedButton(
23
onPressed: () => showCounter.value = true,
24
child: const Text('Show Counter'),
25
);
26
}
27
28
// In this line, the `CounterController` instance will be created
29
final counterController = context.use<CounterController>();
30
31
return Row(
22 collapsed lines
32
mainAxisSize: MainAxisSize.min,
33
children: [
34
ElevatedButton(
35
onPressed: counterController.decrement,
36
child: const Icon(Icons.remove),
37
),
38
const SizedBox(width: 8),
39
// Observes the `count` property of the `counterController`
40
// and rebuilds the widget tree when the `count` value changes
41
ReactterConsumer<CounterController>(
42
listenStates: (counterController) =>
43
[counterController.count],
44
builder: (context, counterController, child) {
45
return Text("${counterController.count}");
46
},
47
),
48
const SizedBox(width: 8),
49
ElevatedButton(
50
onPressed: counterController.increment,
51
child: const Icon(Icons.add),
52
),
53
],
54
);
55
},
56
);
57
},
58
);
59
}
60
}
1
import 'package:reactter/reactter.dart';
2
3
class CounterController {
4
// Create a reactive state using the `Signal` class
5
final count = Signal(0);
6
7
void increment() {
8
count.value++;
9
}
10
11
void decrement() {
12
count.value--;
13
}
14
}
1
import 'package:flutter/material.dart';
2
import 'counter.dart';
3
4
class CounterView extends StatelessWidget {
5
const CounterView({Key? key}) : super(key: key);
6
7
@override
8
Widget build(BuildContext context) {
9
return Scaffold(
10
appBar: AppBar(
11
title: const Text('Counter'),
12
),
13
body: const Center(
14
child: Counter(),
15
),
16
);
17
}
18
}
1
import 'package:flutter/material.dart';
2
import 'counter_view.dart';
3
4
void main() {
5
runApp(MyApp());
6
}
7
8
class MyApp extends StatelessWidget {
9
@override
10
Widget build(BuildContext context) {
11
return MaterialApp(
12
home: CounterView(),
13
);
14
}
15
}

Initialization modes

The ReactterProvider widget provides a mode argument that allows you to initialize the dependency with different modes.

Here’s an example of how to use it:

1
import 'package:flutter/material.dart';
2
import 'package:flutter_reactter/flutter_reactter.dart';
3
import 'counter_controller.dart';
4
5
class Counter extends StatelessWidget {
6
final DependencyMode mode;
7
8
const Counter({Key? key, required this.mode}) : super(key: key);
9
10
@override
11
Widget build(BuildContext context) {
12
// Provides the `CounterController` dependency to the widget tree
13
return ReactterProvider<CounterController>(
14
() => CounterController(),
15
id: mode.toString(),
16
mode: mode,
17
builder: (context, counterController, child) {
24 collapsed lines
18
return Row(
19
mainAxisSize: MainAxisSize.min,
20
children: [
21
ElevatedButton(
22
onPressed: counterController.decrement,
23
child: const Icon(Icons.remove),
24
),
25
const SizedBox(width: 8),
26
// Observes the `count` property of the `counterController`
27
// and rebuilds the widget tree when the `count` value changes
28
ReactterConsumer<CounterController>(
29
id: mode.toString(),
30
listenStates: (counterController) => [counterController.count],
31
builder: (context, counterController, child) {
32
return Text("${counterController.count}");
33
},
34
),
35
const SizedBox(width: 8),
36
ElevatedButton(
37
onPressed: counterController.increment,
38
child: const Icon(Icons.add),
39
),
40
],
41
);
42
},
43
);
44
}
45
}
1
import 'package:Counter/counter_controller.dart';
2
import 'package:flutter/material.dart';
3
import 'package:flutter_reactter/flutter_reactter.dart';
4
import 'counter.dart';
5
6
class CounterView extends StatelessWidget {
7
const CounterView({Key? key}) : super(key: key);
8
9
@override
10
Widget build(BuildContext context) {
11
final showCounter = Signal(false);
12
13
_listenLifecycle();
14
15
return Scaffold(
16
appBar: AppBar(
17
title: const Text('Counter'),
18
),
19
body: Center(
20
child: ReactterWatcher(
21
child: ElevatedButton(
22
onPressed: () => showCounter.value = !showCounter.value,
23
child: showCounter.value
24
? Text("Hide Counters")
25
: Text("Show Counters"),
26
),
27
builder: (context, child) {
28
return Column(
29
children: [
30
child!,
31
if (showCounter.value) ...[
32
const SizedBox(height: 8),
33
// Will register and create the `CounterController` dependency each time.
34
const Counter(
35
mode: DependencyMode.builder,
36
),
37
const SizedBox(height: 8),
38
// Will register the `CounterController` dependency only once.
39
const Counter(
40
mode: DependencyMode.factory,
41
),
42
const SizedBox(height: 8),
43
// Will register and create the `CounterController` dependency only once,
44
// this keeps the counter value between rebuilds
45
const Counter(
46
mode: DependencyMode.singleton,
47
),
48
],
49
],
50
);
51
},
52
),
53
),
54
);
55
}
56
57
void _listenLifecycle() {
58
for (final mode in DependencyMode.values) {
59
Reactter.on(
60
ReactterDependency<CounterController>(
61
mode.toString(),
62
),
63
Lifecycle.registered,
64
(_, __) {
65
print("CounterController registered with ${mode.toString()}.");
66
},
67
);
68
69
Reactter.on(
70
ReactterDependency<CounterController>(
71
mode.toString(),
72
),
73
Lifecycle.created,
74
(_, __) {
75
print("CounterController created with ${mode.toString()}.");
76
},
77
);
78
79
Reactter.on(
80
ReactterDependency<CounterController>(
81
mode.toString(),
82
),
83
Lifecycle.deleted,
84
(_, __) {
85
print("CounterController deleted with ${mode.toString()}.");
86
},
87
);
88
89
Reactter.on(
90
ReactterDependency<CounterController>(
91
mode.toString(),
92
),
93
Lifecycle.unregistered,
94
(_, __) {
95
print("CounterController unregistered with ${mode.toString()}.");
96
},
97
);
98
}
99
}
100
}
1
import 'package:reactter/reactter.dart';
2
3
class CounterController {
4
// Create a reactive state using the `Signal` class
5
final count = Signal(0);
6
7
void increment() {
8
count.value++;
9
}
10
11
void decrement() {
12
count.value--;
13
}
14
}
1
import 'package:flutter/material.dart';
2
import 'counter_view.dart';
3
4
void main() {
5
runApp(MyApp());
6
}
7
8
class MyApp extends StatelessWidget {
9
@override
10
Widget build(BuildContext context) {
11
return MaterialApp(
12
home: CounterView(),
13
);
14
}
15
}