Skip to content

Commit

Permalink
feat!: null safety (rrousselGit#574)
Browse files Browse the repository at this point in the history
  • Loading branch information
felangel authored Feb 17, 2021
1 parent af8ea8a commit 30009af
Show file tree
Hide file tree
Showing 34 changed files with 1,510 additions and 1,295 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ jobs:
channel:
- dev
- beta
- stable

steps:
- uses: actions/checkout@v2

- name: Execute test script
run: curl -s https://raw.githubusercontent.com/rrousselGit/ci/master/scripts/ci.sh | bash
run: curl -s https://raw.githubusercontent.com/rrousselGit/ci/master/scripts/ci.sh | bash -s nnbd
71 changes: 71 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,74 @@
# 5.0.0-nullsafety.4

- Upgraded `nested` dependency to 1.0.0 and `collection` to 1.15.0

# 5.0.0-nullsafety.3

- Improved the error message of `ProviderNotFoundException` to mention hot-reload. (#595)
- Removed the asserts that prevented `ChangeNotifier`s in `ChangeNotifierProvider()`
to have listeners (#596)
- Removed the opinionated asserts in `context.watch`/`context.read`
that prevented them to be used inside specific conditions (#585)

# 5.0.0-nullsafety.2

- Improved the error message when an exception is thrown inside `create` of a provider`

# 5.0.0-nullsafety.1

- Reintroduced `ValueListenableProvider.value` (the default constructor is still removed).

# 5.0.0-nullsafety.0

Migrated Provider to non-nullable types:

- `initialData` for both `FutureProvider` and `StreamProvider` is now required.

To migrate, what used to be:

```dart
FutureProvider<int>(
create: (context) => Future.value(42),
child: MyApp(),
)
Widget build(BuildContext context) {
final value = context.watch<int>();
return Text('$value');
}
```

is now:

```dart
FutureProvider<int?>(
initialValue: null,
create: (context) => Future.value(42),
child: MyApp(),
)
Widget build(BuildContext context) {
// be sure to specify the ? in watch<int?>
final value = context.watch<int?>();
return Text('$value');
}
```

- `ValueListenableProvider` is removed

To migrate, you can instead use `Provider` combined with `ValueListenableBuilder`:

```dart
ValueListenableBuilder<int>(
valueListenable: myValueListenable,
builder: (context, value, _) {
return Provider<int>.value(
value: value,
child: MyApp(),
);
}
)
```
# 4.3.3

- Improved the error message of `ProviderNotFoundException` to mention hot-reload. (#595)
Expand Down
63 changes: 34 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,51 +26,56 @@ See also:
- [flutter architecture sample](https://github.com/brianegan/flutter_architecture_samples/tree/master/change_notifier_provider), which contains an implementation of that app using `provider` + [ChangeNotifier]
- [flutter_bloc](https://github.com/felangel/bloc) and [Mobx](https://github.com/mobxjs/mobx.dart), which use `provider` in their architecture

## Migration from v3.x.0 to v4.0.0
## Migration from 4.x.x to 5.0.0-nullsafety

- The parameters `builder` and `initialBuilder` of providers are removed.
- `initialData` for both `FutureProvider` and `StreamProvider` is now required.

- `initialBuilder` should be replaced by `create`.
- `builder` of "proxy" providers should be replaced by `update`
- `builder` of classical providers should be replaced by `create`.

- The new `create`/`update` callbacks are lazy-loaded, which means they are called
the first time the value is read instead of the first time the provider is created.

If this is undesired, you can disable lazy-loading by passing `lazy: false` to
the provider of your choice:
To migrate, what used to be:

```dart
FutureProvider(
create: (_) async => doSomeHttpRequest(),
lazy: false,
child: ...
FutureProvider<int>(
create: (context) => Future.value(42),
child: MyApp(),
)
Widget build(BuildContext context) {
final value = context.watch<int>();
return Text('$value');
}
```

- `ProviderNotFoundError` is renamed to `ProviderNotFoundException`.
is now:

- The `SingleChildCloneableWidget` interface is removed and replaced by a new kind
of widget `SingleChildWidget`.
```dart
FutureProvider<int?>(
initialValue: null,
create: (context) => Future.value(42),
child: MyApp(),
)
See [this issue](https://github.com/rrousselGit/provider/issues/237) for details
on how to migrate.
Widget build(BuildContext context) {
// be sure to specify the ? in watch<int?>
final value = context.watch<int?>();
return Text('$value');
}
```

- [Selector] now deeply compares the previous and new values if they are collections.
- `ValueListenableProvider` is removed

If this is undesired, you can revert to the old behavior by passing a `shouldRebuild`
parameter to [Selector]:
To migrate, you can instead use `Provider` combined with `ValueListenableBuilder`:

```dart
Selector<Selected, Consumed>(
shouldRebuild: (previous, next) => previous == next,
builder: ...,
ValueListenableBuilder<int>(
valueListenable: myValueListenable,
builder: (context, value, _) {
return Provider<int>.value(
value: value,
child: MyApp(),
);
}
)
```

- `DelegateWidget` and its family is removed. Instead, for custom providers,
directly subclass [InheritedProvider] or an existing provider.

## Usage

### Exposing a value
Expand Down
6 changes: 3 additions & 3 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class Counter with ChangeNotifier, DiagnosticableTreeMixin {
}

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

@override
Widget build(BuildContext context) {
Expand All @@ -50,7 +50,7 @@ class MyApp extends StatelessWidget {
}

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

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -88,7 +88,7 @@ class MyHomePage extends StatelessWidget {
}

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

@override
Widget build(BuildContext context) {
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ author: Remi Rousselet <[email protected]>
publish_to: "none"

environment:
sdk: ">=2.7.0 <3.0.0"
sdk: ">=2.12.0-0.0.dev <3.0.0"

dependencies:
flutter:
Expand Down
16 changes: 8 additions & 8 deletions example/test_driver/app_test.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Imports the Flutter Driver API.
// ignore: import_of_legacy_library_into_null_safe
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';

void main() {
group('counter app', () {
FlutterDriver _driver;
FlutterDriver? _driver;

final incrementFloatingButton =
find.byValueKey('increment_floatingActionButton');
Expand All @@ -22,21 +22,21 @@ void main() {
});

test('AppBar is Flutter Demo Home Page', () async {
expect(await _driver.getText(appBarText), 'Example');
expect(await _driver!.getText(appBarText), 'Example');
});

test('counterText is started with 0', () async {
expect(await _driver.getText(counterState), '0');
expect(await _driver!.getText(counterState), '0');
});

test('pressed increment floating action button twice', () async {
// tap floating action button
await _driver.tap(incrementFloatingButton);
expect(await _driver.getText(counterState), '1');
await _driver!.tap(incrementFloatingButton);
expect(await _driver!.getText(counterState), '1');

// tap floating action button
await _driver.tap(incrementFloatingButton);
expect(await _driver.getText(counterState), '2');
await _driver!.tap(incrementFloatingButton);
expect(await _driver!.getText(counterState), '2');
});
});
}
Loading

0 comments on commit 30009af

Please sign in to comment.