Skip to content

BuildContext Extensions

The BuildContext class in Flutter provides information about the current widget in the tree. Reactter extends the BuildContext class with additional methods to make it easier to work with dependencies.

Reactter provides the following extensions for the BuildContext class:

BuildContext.use

BuildContext.use is a method that gets an instance of T dependency from the nearest ancestor provider ( RtProvider or RtComponent ) of type T .

Syntax

T context.use<T>([String? id]);
  • context: The BuildContext object, which provides information about the current widget in the tree.
  • T : The type of the dependency you want to access. These are some points to consider:
    • If the type is nullable, the method will return null if the dependency is not found.
    • If the type is not nullable, the method will throw an exception if the dependency is not found.
  • id: An optional identifier for the T dependency. If omitted, the dependency will be located by its type ( T ).

Usage

This following example demonstrates how to use BuildContext.use :

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, this.id}) : super(key: key);
7
8
final String? id;
9
10
@override
11
Widget build(BuildContext context) {
12
// Locale the `CounterController` dependency
13
final counterController = context.use<CounterController>(id);
29 collapsed lines
14
15
return Row(
16
mainAxisSize: MainAxisSize.min,
17
children: [
18
ElevatedButton(
19
onPressed: counterController.decrement,
20
child: const Icon(Icons.remove),
21
),
22
const SizedBox(width: 8),
23
// Observe the `count` property of the `counterController`
24
// and rebuild the widget tree when the `count` value changes
25
Builder(
26
builder: (context) {
27
if (id != null) {
28
context.watchId<CounterController>(id!, (inst) => [inst.count]);
29
} else {
30
context.watch<CounterController>((inst) => [inst.count]);
31
}
32
33
return Text("${counterController.count}");
34
},
35
),
36
const SizedBox(width: 8),
37
ElevatedButton(
38
onPressed: counterController.increment,
39
child: const Icon(Icons.add),
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: [
17
const Counter(),
18
const SizedBox(height: 8),
19
FutureBuilder(
20
future: Future.delayed(Duration(seconds: 1)),
21
builder: (context, snapshot) {
22
if (snapshot.connectionState != ConnectionState.done) {
23
return const CircularProgressIndicator();
24
}
25
26
return const Counter(id: "counterLazy");
27
},
28
),
29
],
30
),
31
),
32
);
33
}
34
}
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 'package:flutter_reactter/flutter_reactter.dart';
3
import 'counter_controller.dart';
4
import 'counter_view.dart';
5
6
void main() {
7
runApp(MyApp());
8
}
9
10
class MyApp extends StatelessWidget {
11
@override
12
Widget build(BuildContext context) {
13
return MaterialApp(
14
home: RtMultiProvider(
15
[
16
RtProvider(() => CounterController()),
17
RtProvider.lazy(() => CounterController(), id: 'counterLazy'),
18
],
19
child: CounterView(),
20
),
21
);
22
}
23
}

In this example, the Counter widget uses BuildContext.use to get the instance of CounterController from the nearest ancestor provider (located in main.dart).

BuildContext.watch

BuildContext.watch is a method similar to BuildContext.use , but with an added functionality. It not only retrieves the T dependency but also registers the current widget as a listener for any changes to the states set or otherwise any changes in the T dependency. Consequently, whenever changes, the widget using BuildContext.watch is automatically notified and rebuilt.

Syntax

T context.watch<T>([
List<RtState> listenStates?(T instance),
String? id,
]);
  • context: The BuildContext object, which provides information about the current widget in the tree.
  • T : The type of the dependency you want to access. These are some points to consider:
    • If the type is nullable, the method will return null if the dependency is not found.
    • If the type is not nullable, the method will throw an exception if the dependency is not found.
  • listenStates : An optional function that returns a list of state ( RtState ) to listen for changes. If omitted, the method will listen for any changes in the T dependency.
  • id: An optional identifier for the T dependency. If omitted, the dependency will be located by its type ( T ).

Usage

This following example demonstrates how to use BuildContext.watch :

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, this.id}) : super(key: key);
7
8
final String? id;
9
10
@override
11
Widget build(BuildContext context) {
12
// Locale the `CounterController` dependency
13
final counterController = context.use<CounterController>(id);
14
15
return Row(
16
mainAxisSize: MainAxisSize.min,
17
children: [
18
ElevatedButton(
19
onPressed: counterController.decrement,
20
child: const Icon(Icons.remove),
21
),
22
const SizedBox(width: 8),
23
// Observe the `count` property of the `counterController`
24
// and rebuild the widget tree when the `count` value changes
25
Builder(
26
builder: (context) {
27
if (id != null) {
28
context.watchId<CounterController>(id!, (inst) => [inst.count]);
29
} else {
30
context.watch<CounterController>((inst) => [inst.count]);
31
}
32
33
return Text("${counterController.count}");
34
},
35
),
36
const SizedBox(width: 8),
37
ElevatedButton(
38
onPressed: counterController.increment,
39
child: const Icon(Icons.add),
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: [
17
const Counter(),
18
const SizedBox(height: 8),
19
FutureBuilder(
20
future: Future.delayed(Duration(seconds: 1)),
21
builder: (context, snapshot) {
22
if (snapshot.connectionState != ConnectionState.done) {
23
return const CircularProgressIndicator();
24
}
25
26
return const Counter(id: "counterLazy");
27
},
28
),
29
],
30
),
31
),
32
);
33
}
34
}
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 'package:flutter_reactter/flutter_reactter.dart';
3
import 'counter_controller.dart';
4
import 'counter_view.dart';
5
6
void main() {
7
runApp(MyApp());
8
}
9
10
class MyApp extends StatelessWidget {
11
@override
12
Widget build(BuildContext context) {
13
return MaterialApp(
14
home: RtMultiProvider(
15
[
16
RtProvider(() => CounterController()),
17
RtProvider.lazy(() => CounterController(), id: 'counterLazy'),
18
],
19
child: CounterView(),
20
),
21
);
22
}
23
}

In this example, we use BuildContext.watch to get the instance of CounterController from the nearest ancestor provider (located in main.dart) and listen for changes in the count state.

BuildContext.watchId

BuildContext.watchId is a method similar to BuildContext.watch , but use id as first argument to locate the dependency.

Syntax

T context.watchId<T>(
String id,
[List<RtState> listenStates?(T instance)],
);
  • context: The BuildContext object, which provides information about the current widget in the tree.
  • T : The type of the dependency you want to access. These are some points to consider:
    • If the type is nullable, the method will return null if the dependency is not found.
    • If the type is not nullable, the method will throw an exception if the dependency is not found.
  • id: An identifier for the T dependency. The dependency will be located by its type ( T ) and the id.
  • listenStates : An optional function that returns a list of state ( RtState ) to listen for changes. If omitted, the method will listen for any changes in the T dependency.

Usage

This following example demonstrates how to use BuildContext.watchId :

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, this.id}) : super(key: key);
7
8
final String? id;
9
10
@override
11
Widget build(BuildContext context) {
12
// Locale the `CounterController` dependency
13
final counterController = context.use<CounterController>(id);
14
15
return Row(
16
mainAxisSize: MainAxisSize.min,
17
children: [
18
ElevatedButton(
19
onPressed: counterController.decrement,
20
child: const Icon(Icons.remove),
21
),
22
const SizedBox(width: 8),
23
// Observe the `count` property of the `counterController`
24
// and rebuild the widget tree when the `count` value changes
25
Builder(
26
builder: (context) {
27
if (id != null) {
28
context.watchId<CounterController>(id!, (inst) => [inst.count]);
29
} else {
30
context.watch<CounterController>((inst) => [inst.count]);
31
}
32
33
return Text("${counterController.count}");
34
},
35
),
36
const SizedBox(width: 8),
37
ElevatedButton(
38
onPressed: counterController.increment,
39
child: const Icon(Icons.add),
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: [
17
const Counter(),
18
const SizedBox(height: 8),
19
FutureBuilder(
20
future: Future.delayed(Duration(seconds: 1)),
21
builder: (context, snapshot) {
22
if (snapshot.connectionState != ConnectionState.done) {
23
return const CircularProgressIndicator();
24
}
25
26
return const Counter(id: "counterLazy");
27
},
28
),
29
],
30
),
31
),
32
);
33
}
34
}
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 'package:flutter_reactter/flutter_reactter.dart';
3
import 'counter_controller.dart';
4
import 'counter_view.dart';
5
6
void main() {
7
runApp(MyApp());
8
}
9
10
class MyApp extends StatelessWidget {
11
@override
12
Widget build(BuildContext context) {
13
return MaterialApp(
14
home: RtMultiProvider(
15
[
16
RtProvider(() => CounterController()),
17
RtProvider.lazy(() => CounterController(), id: 'counterLazy'),
18
],
19
child: CounterView(),
20
),
21
);
22
}
23
}

In this example, the Counter widget uses BuildContext.watchId to get the instance of CounterController by its id from the nearest ancestor provider (located in main.dart) and listen for changes in the count state.

BuildContext.select

BuildContext.select is a method that computes a value whenever the selected states change and registers the current widget as a listener for these changes. Consequently, each time the selected states change, the value is recalculated and if it is different from the previous one, the widget is automatically notified and rebuilt.

Syntax

V context.select<T, V>(
V computeValue(T instance, RtState select(RtState state)),
[String? id],
);
  • context: The BuildContext object, which provides information about the current widget in the tree.
  • T : The type of the dependency you want to select. It is used to locate the dependency from the nearest ancestor provider.
  • V : The type of the value you want to compute. It is the return type of the computeValue function.
  • computeValue : A function that computes the value based on the selected states. It takes two parameters:
    • instance: The instance of the dependency of type T .
    • select : A function that allows you to wrap the state ( RtState ) to be listened for changes and returns it.
  • id: An optional identifier for the T dependency. If omitted, the dependency will be located by its type ( T ).

Usage

This following example demonstrates how to use BuildContext.select :

1
import 'package:flutter/widgets.dart';
2
import 'package:flutter_reactter/flutter_reactter.dart';
3
import 'counter_controller.dart';
4
5
class CounterDivisible extends StatelessWidget {
6
final int byNum;
7
8
const CounterDivisible({Key? key, required this.byNum}) : super(key: key);
9
10
@override
11
Widget build(BuildContext context) {
12
// Select the `count` state from the `CounterController`,
13
// calculate if the `count` is divisible by num(`byNum`)
14
// and rebuild the widget tree when the value(`isDivisibleByNum`) changes
15
final isDivisibleByNum = context.select<CounterController, bool>(
16
(counterController, $) => $(counterController.count).value % byNum == 0,
17
);
18
19
return Text(
20
isDivisibleByNum ? "Divisible by $byNum" : "Not divisible by $byNum",
21
);
22
}
23
}
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: [
17
const Counter(),
18
const SizedBox(height: 8),
19
FutureBuilder(
20
future: Future.delayed(Duration(seconds: 1)),
21
builder: (context, snapshot) {
22
if (snapshot.connectionState != ConnectionState.done) {
23
return const CircularProgressIndicator();
24
}
25
26
return const Counter(id: "counterLazy");
27
},
28
),
29
],
30
),
31
),
32
);
33
}
34
}
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, this.id}) : super(key: key);
7
8
final String? id;
9
10
@override
11
Widget build(BuildContext context) {
12
// Locale the `CounterController` dependency
13
final counterController = context.use<CounterController>(id);
14
15
return Row(
16
mainAxisSize: MainAxisSize.min,
17
children: [
18
ElevatedButton(
19
onPressed: counterController.decrement,
20
child: const Icon(Icons.remove),
21
),
22
const SizedBox(width: 8),
23
// Observe the `count` property of the `counterController`
24
// and rebuild the widget tree when the `count` value changes
25
Builder(
26
builder: (context) {
27
if (id != null) {
28
context.watchId<CounterController>(id!, (inst) => [inst.count]);
29
} else {
30
context.watch<CounterController>((inst) => [inst.count]);
31
}
32
33
return Text("${counterController.count}");
34
},
35
),
36
const SizedBox(width: 8),
37
ElevatedButton(
38
onPressed: counterController.increment,
39
child: const Icon(Icons.add),
40
),
41
],
42
);
43
}
44
}
1
import 'package:flutter/material.dart';
2
import 'package:flutter_reactter/flutter_reactter.dart';
3
import 'counter_controller.dart';
4
import 'counter_view.dart';
5
6
void main() {
7
runApp(MyApp());
8
}
9
10
class MyApp extends StatelessWidget {
11
@override
12
Widget build(BuildContext context) {
13
return MaterialApp(
14
home: RtMultiProvider(
15
[
16
RtProvider(() => CounterController()),
17
RtProvider.lazy(() => CounterController(), id: 'counterLazy'),
18
],
19
child: CounterView(),
20
),
21
);
22
}
23
}
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
}

In this example, checks if the count state from CounterController is divisible by a specified number (byNum). The BuildContext.select method is used to perform this check and rebuild the widget tree whenever the divisibility status changes.