Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/animations/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.3.0

* Added OpenContainerPage and onOpen hook to OpenContainer for go_router compatibility.

## 2.2.0

* Adds support for custom `closedShadows` and `openShadows` to `OpenContainer`.
Expand Down
28 changes: 28 additions & 0 deletions packages/animations/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,31 @@ _Examples of the fade pattern:_
2. _A menu_
3. _A snackbar_
4. _A FAB_

## Usage with Declarative Routers (e.g. go_router)

The `OpenContainer` widget can be integrated with declarative routers like `go_router` to ensure that the browser URL updates when the container opens, while still preserving the container transform animation.

To do this, use the `onOpen` hook to trigger the navigation and `OpenContainerPage` in your route definition. Both must share the same `transitionTag`.

```dart
// In your list page:
OpenContainer(
transitionTag: 'item-${item.id}',
onOpen: () => context.push('/details/${item.id}'),
closedBuilder: (context, action) => MyListTile(onTap: action),
openBuilder: (context, action) => MyDetailsPage(id: item.id),
)

// In your router configuration:
GoRoute(
path: '/details/:id',
pageBuilder: (context, state) {
final String id = state.pathParameters['id']!;
return OpenContainerPage(
transitionTag: 'item-$id',
openBuilder: (context, action) => MyDetailsPage(id: id),
);
},
)
```
118 changes: 118 additions & 0 deletions packages/animations/example/lib/go_router_example.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:animations/animations.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

void main() {
runApp(const GoRouterExampleApp());
}

/// An example of integrating package:animations with package:go_router.
class GoRouterExampleApp extends StatelessWidget {
/// Creates a [GoRouterExampleApp].
const GoRouterExampleApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'OpenContainer go_router Example',
routerConfig: _router,
);
}
}

final GoRouter _router = GoRouter(
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) {
return const HomeScreen();
},
routes: <RouteBase>[
GoRoute(
path: 'details/:id',
pageBuilder: (BuildContext context, GoRouterState state) {
final String id = state.pathParameters['id']!;
return OpenContainerPage<void>(
transitionTag: 'item-$id',
openBuilder: (BuildContext context, VoidCallback closeContainer) {
return DetailsScreen(id: id);
},
);
},
),
],
),
],
);

/// The home screen of the go_router example.
class HomeScreen extends StatelessWidget {
/// Creates a [HomeScreen].
const HomeScreen({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('OpenContainer go_router Example')),
body: ListView.builder(
itemCount: 20,
itemBuilder: (BuildContext context, int index) {
final id = index.toString();
return OpenContainer(
transitionTag: 'item-$id',
onOpen: () {
context.go('/details/$id');
return Future<void>.value();
},
closedBuilder: (BuildContext context, VoidCallback openContainer) {
return ListTile(
onTap: openContainer,
title: Text('Item $id'),
subtitle: const Text('Tap to open with container transform'),
);
},
openBuilder: (BuildContext context, VoidCallback closeContainer) {
return DetailsScreen(id: id);
},
);
},
),
);
}
}

/// The details screen of the go_router example.
class DetailsScreen extends StatelessWidget {
/// Creates a [DetailsScreen].
const DetailsScreen({super.key, required this.id});

/// The ID of the item to display.
final String id;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Details of Item $id')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Details for Item $id',
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => context.pop(),
child: const Text('Go Back'),
),
],
),
),
);
}
}
14 changes: 14 additions & 0 deletions packages/animations/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:flutter/scheduler.dart';
import 'container_transition.dart';
import 'fade_scale_transition.dart';
import 'fade_through_transition.dart';
import 'go_router_example.dart';
import 'shared_axis_transition.dart';

void main() {
Expand Down Expand Up @@ -94,6 +95,19 @@ class _TransitionsHomePageState extends State<_TransitionsHomePage> {
);
},
),
_TransitionListTile(
title: 'go_router integration',
subtitle: 'OpenContainerPage',
onTap: () {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
return const GoRouterExampleApp();
},
),
);
},
),
],
),
),
Expand Down
1 change: 1 addition & 0 deletions packages/animations/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies:
cupertino_icons: ^1.0.2
flutter:
sdk: flutter
go_router: ^14.0.0

dev_dependencies:
flutter_test:
Expand Down
Loading
Loading