This PR introduces `AnimationStyle`, it is used to override default animation curves and durations in several widgets.
fixes [Add the ability to customize MaterialApp theme animation duration](https://github.com/flutter/flutter/issues/78372)
fixes [Allow customization of showMenu transition animation curves and duration](https://github.com/flutter/flutter/issues/135638)
Here is an example where popup menu curve and transition duration is overriden:
```dart
popUpAnimationStyle: AnimationStyle(
curve: Easing.emphasizedAccelerate,
duration: Durations.medium4,
),
```
Set `AnimationStyle.noAnimation` to disable animation.
```dart
return MaterialApp(
themeAnimationStyle: AnimationStyle.noAnimation,
```
Currently, `Switch.factory` delegates to `CupertinoSwitch` when platform
is iOS or macOS. This PR is to:
* have the factory configure the Material `Switch` for the expected look
and feel.
* introduce `Adaptation` class to customize themes for the adaptive
components.
Updated the NavigationBar API doc that describes
examples/api/lib/material/navigation_bar/navigation_bar.2.dart and made
some cosmetic changes to the example to improve its appearance in
Material 3. Also did a little gratuitous reformatting.
Fixes#136125
Fixes#119401
This PR is to:
* add `Card.filled` and `Card.outlined` factory methods so that we can use tokens for these two types of cards to generate default theme instead of providing hard-corded values in example.
* update card.2.dart example.
* add test file for card.2.dart example.
* fix some mismatch caused by editing the auto-generated defaults by hand in navigation_bar.dart and navigation_drawer.dart.
*Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.*
fix some typos
*List which issues are fixed by this PR. You must list at least one issue.*
*If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].*
## Description
This converts the `MenuAnchor` class to use `OverlayPortal` instead of directly using the overlay.
## Related Issues
- Fixes https://github.com/flutter/flutter/issues/124830
## Tests
- No tests yet (hence it is a draft)
## Description
In order for `MenuAnchor` menus to be able to not pass on the taps that close their menus, `TapRegion` needed a way to consume them. This change adds a flag to the `TapRegion`, `consumeOutsideTap` that will consume taps that occur outside of the region if the flag is set (it is false by default). The same flag is added to `MenuAnchor` to allow selecting the behavior for menus.
`TapRegion` consumes the tap event by registering with the gesture arena and immediately resolving the tap as accepted if any regions in a group have `consumeOutsideTap` set to true.
This PR also deprecates `MenuAnchor.anchorTapClosesMenu`, since it is a much more limited version of the same feature that only applied to the anchor itself, and even then only applied to closing the menu, not passing along the tap. The same functionality can now be implemented by handling a tap on the anchor widget and checking to see if the menu is open before closing it.
## Related Issues
- https://github.com/flutter/flutter/issues/135327
## Tests
- Added tests for `TapRegion` to make sure taps are consumed properly.
New example for `InputChip` that demonstrate how to create/delete them based on user text inputs.
The sample application shows a custom text area where user can enter text. After the user has typed and hits _Enter_ the text will be replaced with an `InputChip` that contains that text. Is it possible to continue typing and add more chips in this way. All of them will be placed in a scrollable horizontal row. Also is it possible to have suggestion displayed below the text input field in case the typed text match some of the available suggestions.
Issue I'm trying to solve:
- https://github.com/flutter/flutter/issues/128247
**Code structure:**
The example app is composed of 2 main components that find places inside `MainScreen`:
- `ChipsInput`
- `ListView`
`ChipsInput` emulates a `TextField` where you can enter text. This text field accepts also a list of values of generic type T (`Topping` in my example), that gets rendered as `InputChip` inside the text field, before the text inserted by the user. This widgets is basically an `InputDecorator` widget that implements `TextInputClient` to get `TextEditingValue` events from the user keyboard. At the end of the input field there is another component, the `TextCursor`, that is displayed just when the user give the focus to the field and emulates the carrets that `TextField` has.
There are also some available callbacks that the user can use to capture events in the `ChipsInput` field like: `onChanged`, `onChipTapped`, `onSubmitted` and `onTextChanged`. This last callback is used to build a list of suggestion that will be placed just below the `ChipsInput` field inside the `ListView`.
- slightly improved assert message when row cell counts don't match column count.
- more breadcrumbs in API documentation. more documentation in general.
- added more documentation for the direction of the "ascending" arrow.
- two samples for PaginatedDataTable.
- make PaginatedDataTable support hot reloading across changes to the number of columns.
- introduce matrix3MoreOrLessEquals. An earlier version of this PR used it in tests, but eventually it was not needed. The function seems useful to keep though.
This PR aims to support Android's predictive back gesture when popping the entire Flutter app. Predictive route transitions between routes inside of a Flutter app will come later.
<img width="200" src="https://user-images.githubusercontent.com/389558/217918109-945febaa-9086-41cc-a476-1a189c7831d8.gif" />
### Trying it out
If you want to try this feature yourself, here are the necessary steps:
1. Run Android 33 or above.
1. Enable the feature flag for predictive back on the device under "Developer
options".
1. Create a Flutter project, or clone [my example project](https://github.com/justinmc/flutter_predictive_back_examples).
1. Set `android:enableOnBackInvokedCallback="true"` in
android/app/src/main/AndroidManifest.xml (already done in the example project).
1. Check out this branch.
1. Run the app. Perform a back gesture (swipe from the left side of the
screen).
You should see the predictive back animation like in the animation above and be able to commit or cancel it.
### go_router support
go_router works with predictive back out of the box because it uses a Navigator internally that dispatches NavigationNotifications!
~~go_router can be supported by adding a listener to the router and updating SystemNavigator.setFrameworkHandlesBack.~~
Similar to with nested Navigators, nested go_routers is supported by using a PopScope widget.
<details>
<summary>Full example of nested go_routers</summary>
```dart
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:go_router/go_router.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
void main() => runApp(_MyApp());
class _MyApp extends StatelessWidget {
final GoRouter router = GoRouter(
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) => _HomePage(),
),
GoRoute(
path: '/nested_navigators',
builder: (BuildContext context, GoRouterState state) => _NestedGoRoutersPage(),
),
],
);
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: router,
);
}
}
class _HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Nested Navigators Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Home Page'),
const Text('A system back gesture here will exit the app.'),
const SizedBox(height: 20.0),
ListTile(
title: const Text('Nested go_router route'),
subtitle: const Text('This route has another go_router in addition to the one used with MaterialApp above.'),
onTap: () {
context.push('/nested_navigators');
},
),
],
),
),
);
}
}
class _NestedGoRoutersPage extends StatefulWidget {
@override
State<_NestedGoRoutersPage> createState() => _NestedGoRoutersPageState();
}
class _NestedGoRoutersPageState extends State<_NestedGoRoutersPage> {
late final GoRouter _router;
final GlobalKey<NavigatorState> _nestedNavigatorKey = GlobalKey<NavigatorState>();
// If the nested navigator has routes that can be popped, then we want to
// block the root navigator from handling the pop so that the nested navigator
// can handle it instead.
bool get _popEnabled {
// canPop will throw an error if called before build. Is this the best way
// to avoid that?
return _nestedNavigatorKey.currentState == null ? true : !_router.canPop();
}
void _onRouterChanged() {
// Here the _router reports the location correctly, but canPop is still out
// of date. Hence the post frame callback.
SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
setState(() {});
});
}
@override
void initState() {
super.initState();
final BuildContext rootContext = context;
_router = GoRouter(
navigatorKey: _nestedNavigatorKey,
routes: [
GoRoute(
path: '/',
builder: (BuildContext context, GoRouterState state) => _LinksPage(
title: 'Nested once - home route',
backgroundColor: Colors.indigo,
onBack: () {
rootContext.pop();
},
buttons: <Widget>[
TextButton(
onPressed: () {
context.push('/two');
},
child: const Text('Go to another route in this nested Navigator'),
),
],
),
),
GoRoute(
path: '/two',
builder: (BuildContext context, GoRouterState state) => _LinksPage(
backgroundColor: Colors.indigo.withBlue(255),
title: 'Nested once - page two',
),
),
],
);
_router.addListener(_onRouterChanged);
}
@override
void dispose() {
_router.removeListener(_onRouterChanged);
super.dispose();
}
@override
Widget build(BuildContext context) {
return PopScope(
popEnabled: _popEnabled,
onPopped: (bool success) {
if (success) {
return;
}
_router.pop();
},
child: Router<Object>.withConfig(
restorationScopeId: 'router-2',
config: _router,
),
);
}
}
class _LinksPage extends StatelessWidget {
const _LinksPage ({
required this.backgroundColor,
this.buttons = const <Widget>[],
this.onBack,
required this.title,
});
final Color backgroundColor;
final List<Widget> buttons;
final VoidCallback? onBack;
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: backgroundColor,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(title),
//const Text('A system back here will go back to Nested Navigators Page One'),
...buttons,
TextButton(
onPressed: onBack ?? () {
context.pop();
},
child: const Text('Go back'),
),
],
),
),
);
}
}
```
</details>
### Resources
Fixes https://github.com/flutter/flutter/issues/109513
Depends on engine PR https://github.com/flutter/engine/pull/39208✔️
Design doc: https://docs.google.com/document/d/1BGCWy1_LRrXEB6qeqTAKlk-U2CZlKJ5xI97g45U7azk/edit#
Migration guide: https://github.com/flutter/website/pull/8952
Deprecate `textScaleFactor` in favor of `textScaler`, in preparation for Android 14 [Non-linear font scaling to 200%](https://developer.android.com/about/versions/14/features#non-linear-font-scaling). The `TextScaler` class can be moved to `dart:ui` in the future, if we decide to use the Android platform API or AndroidX to get the scaling curve instead of hard coding the curve in the framework.
I haven't put the Flutter version in the deprecation message so the analyzer checks are failing. Will do so after I finish the migration guide.
**Why `TextScaler.textScaleFactor`**
The author of a `TextScaler` subclass should provide a fallback `textScaleFactor`. By making `TextScaler` also contain the `textScaleFactor` information it also makes it easier to migrate: if a widget overrides `MediaQueryData.textScaler` in the tree, for unmigrated widgets in the subtree it would also have to override `MediaQueryData.textScaleFactor`, and that makes it difficult to remove `MediaQueryData.textScaleFactor` in the future.
## A full list of affected APIs in this PR
Deprecated: The method/getter/setter/argument is annotated with a `@Deprecated()` annotation in this PR, and the caller should replace it with `textScaler` instead. Unless otherwise specified there will be a Flutter fix available to help with migration but it's still recommended to migrate case-by-case.
**Replaced**: The method this `textScaleFactor` argument belongs to is rarely called directly by user code and is not overridden by any of the registered custom tests, so the argument is directly replaced by `TextScaler`.
**To Be Deprecated**: The method/getter/setter/argument can't be deprecated in this PR because a registered customer test depends on it and a Flutter fix isn't available (or the test was run without applying flutter fixes first). This method/getter/setter/argument will be deprecated in a followup PR once the registered test is migrated.
### `Painting` Library
| Affected API | State of `textScaleFactor` | Comment |
| --- | --- | --- |
| `InlineSpan.build({ double textScaleFactor = 1.0 })` argument | **Replaced** | |
| `TextStyle.getParagraphStyle({ double TextScaleFactor = 1.0 })` argument | **Replaced** | |
| `TextStyle.getTextStyle({ double TextScaleFactor = 1.0 })` argument| Deprecated | Can't replace: c47fd38dca/super_editor/lib/src/infrastructure/super_textfield/desktop/desktop_textfield.dart (L1903-L1905)|
| `TextPainter({ double TextScaleFactor = 1.0 })` constructor argument | Deprecated | |
| `TextPainter.textScaleFactor` getter and setter | Deprecated | No Flutter Fix, not expressible yet |
| `TextPainter.computeWidth({ double TextScaleFactor = 1.0 })` argument | Deprecated | |
| `TextPainter.computeMaxIntrinsicWidth({ double TextScaleFactor = 1.0 })` argument | Deprecated | |
### `Rendering` Library
| Affected API | State of `textScaleFactor` | Comment |
| --- | --- | --- |
| `RenderEditable({ double TextScaleFactor = 1.0 })` constructor argument | Deprecated | |
| `RenderEditable.textScaleFactor` getter and setter | Deprecated | No Flutter Fix, not expressible yet |
| `RenderParagraph({ double TextScaleFactor = 1.0 })` constructor argument | Deprecated | |
| `RenderParagraph.textScaleFactor` getter and setter | Deprecated | No Flutter Fix, not expressible yet |
### `Widgets` Library
| Affected API | State of `textScaleFactor` | Comment |
| --- | --- | --- |
| `MediaQueryData({ double TextScaleFactor = 1.0 })` constructor argument | **To Be Deprecated** | cd7b93532e/packages/flutter_markdown/test/text_scale_factor_test.dart (LL39C21-L39C35) |
| `MediaQueryData.textScaleFactor` getter | Deprecated | |
| `MediaQueryData.copyWith({ double? TextScaleFactor })` argument | Deprecated | |
| `MediaQuery.maybeTextScaleFactorOf(BuildContext context)` static method | Deprecated | No Flutter Fix, not expressible yet |
| `MediaQuery.textScaleFactorOf(BuildContext context)` static method | **To Be Deprecated** | cd7b93532e/packages/flutter_markdown/lib/src/_functions_io.dart (L68-L70), No Flutter Fix, not expressible yet |
| `RichText({ double TextScaleFactor = 1.0 })` constructor argument | **To Be Deprecated** | cd7b93532e/packages/flutter_markdown/lib/src/builder.dart (L829-L843) |
| `RichText.textScaleFactor` getter | **To Be Deprecated** | A constructor argument can't be deprecated right away|
| `Text({ double? TextScaleFactor = 1.0 })` constructor argument | **To Be Deprecated** | 914d120da1/packages/rfw/lib/src/flutter/core_widgets.dart (L647) , No Flutter Fix because of https://github.com/dart-lang/sdk/issues/52664 |
| `Text.rich({ double? TextScaleFactor = 1.0 })` constructor argument | **To Be Deprecated** | The default constructor has an argument that can't be deprecated right away. No Flutter Fix because of https://github.com/dart-lang/sdk/issues/52664 |
| `Text.textScaleFactor` getter | **To Be Deprecated** | A constructor argument can't be deprecated right away |
| `EditableText({ double? TextScaleFactor = 1.0 })` constructor argument | Deprecated | No Flutter Fix because of https://github.com/dart-lang/sdk/issues/52664 |
| `EditableText.textScaleFactor` getter | Deprecated | |
### `Material` Library
| Affected API | State of `textScaleFactor` | Comment |
| --- | --- | --- |
| `SelectableText({ double? TextScaleFactor = 1.0 })` constructor argument | **To Be Deprecated** | cd7b93532e/packages/flutter_markdown/lib/src/builder.dart (L829-L843), No Flutter Fix because of https://github.com/dart-lang/sdk/issues/52664 |
| `SelectableText.rich({ double? TextScaleFactor = 1.0 })` constructor argument | **To Be Deprecated** | The default constructor has an argument that can't be deprecated right away. No Flutter Fix because of https://github.com/dart-lang/sdk/issues/52664 |
| `SelectableText.textScaleFactor` getter | **To Be Deprecated** | A constructor argument can't be deprecated right away |
A lot of material widgets (`Slider`, `RangeSlider`, `TimePicker`, and different types of buttons) also change their layout based on `textScaleFactor`. These need to be handled in a case-by-case fashion and will be migrated in follow-up PRs.
This PR changes the example app into a custom scrollview with three slivers. The middle sliver has a nested scrollview of height 300 and only this nested sliver can trigger the refresh indicator.
Fixes https://github.com/flutter/flutter/issues/116237.
This change updates `SelectableRegion`s right-click gesture to match native platform behavior.
Before: Right-click gesture selects word at position and opens context menu (All Platforms)
After:
- Linux, toggles context menu on/off, and collapses selection when click was not on an active selection (uncollapsed).
- Windows, Android, Fuchsia, shows context menu at right-clicked position (unless the click is at an active selection).
- macOS, toggles the context menu if right click was at the same position as the previous / or selects word at position and opens context menu.
- iOS, selects word at position and opens context menu.
This change also prevents the `copy` menu button from being shown when there is a collapsed selection (nothing to copy).
Fixes#117561
## Description
Changes the context menu example for `MenuAnchor` so that it uses right-click, or (on macOS and iOS only) ctrl-left-click, for the context menu. Also disables the browser context menu on web platforms.
## Tests
- Updated test to reflect new triggers.
Updated the ToggleButtons example and test to M3.
Updated the nested tabs test to avoid warnings like:
```
Warning: A call to tap() with finder "exactly one widget with type "Tab" which is an ancestor of text "Explore": Tab(text: "Explore", icon: Icon)" derived an Offset (Offset(666.7, 92.0)) that would not hit test on the specified widget.
```
This example was incorrectly throwing away results from a query when multiple queries were pending at once. Thanks to @sun-jiao in https://github.com/flutter/flutter/pull/127019#issuecomment-1552347037 for pointing this out.
I also added a quick `Text` widget explaining what to do to use the examples. Since there are only three small possible `options`, it's easy to type into the field and not get any results and wonder what's wrong.