1
0
mirror of https://github.com/flutter/samples.git synced 2025-11-08 13:58:47 +00:00

Flutter 3.29 beta (#2571)

This commit is contained in:
Eric Windmill
2025-02-12 18:08:01 -05:00
committed by GitHub
parent d62c784789
commit 719fd72c38
685 changed files with 76244 additions and 53721 deletions

View File

@@ -7,10 +7,7 @@ import 'context_menu_region.dart';
import 'platform_selector.dart';
class AnywherePage extends StatelessWidget {
AnywherePage({
super.key,
required this.onChangedPlatform,
});
AnywherePage({super.key, required this.onChangedPlatform});
static const String route = 'anywhere';
static const String title = 'Context Menu Anywhere Example';
@@ -36,9 +33,7 @@ class AnywherePage extends StatelessWidget {
appBar: AppBar(
title: const Text(AnywherePage.title),
actions: <Widget>[
PlatformSelector(
onChangedPlatform: onChangedPlatform,
),
PlatformSelector(onChangedPlatform: onChangedPlatform),
IconButton(
icon: const Icon(Icons.code),
onPressed: () async {

View File

@@ -10,10 +10,7 @@ import 'platform_selector.dart';
// https://master-api.flutter.dev/flutter/material/MenuBar-class.html
class CascadingMenuPage extends StatelessWidget {
const CascadingMenuPage({
super.key,
required this.onChangedPlatform,
});
const CascadingMenuPage({super.key, required this.onChangedPlatform});
static const String route = 'cascading';
static const String title = 'Cascading Menu Example';
@@ -29,9 +26,7 @@ class CascadingMenuPage extends StatelessWidget {
appBar: AppBar(
title: const Text(CascadingMenuPage.title),
actions: <Widget>[
PlatformSelector(
onChangedPlatform: onChangedPlatform,
),
PlatformSelector(onChangedPlatform: onChangedPlatform),
IconButton(
icon: const Icon(Icons.code),
onPressed: () async {
@@ -117,7 +112,8 @@ class _MyContextMenuRegionState extends State<_MyContextMenuRegion> {
),
),
Text(
_lastSelection != null ? 'Last Selected: $_lastSelection' : ''),
_lastSelection != null ? 'Last Selected: $_lastSelection' : '',
),
],
),
),
@@ -130,10 +126,15 @@ class _MyContextMenuRegionState extends State<_MyContextMenuRegion> {
/// This sort of class is not required, but illustrates one way that defining
/// menus could be done.
class MenuEntry {
const MenuEntry(
{required this.label, this.shortcut, this.onPressed, this.menuChildren})
: assert(menuChildren == null || onPressed == null,
'onPressed is ignored if menuChildren are provided');
const MenuEntry({
required this.label,
this.shortcut,
this.onPressed,
this.menuChildren,
}) : assert(
menuChildren == null || onPressed == null,
'onPressed is ignored if menuChildren are provided',
);
final String label;
final MenuSerializableShortcut? shortcut;
@@ -159,7 +160,8 @@ class MenuEntry {
}
static Map<MenuSerializableShortcut, Intent> shortcuts(
List<MenuEntry> selections) {
List<MenuEntry> selections,
) {
final Map<MenuSerializableShortcut, Intent> result =
<MenuSerializableShortcut, Intent>{};
for (final MenuEntry selection in selections) {
@@ -167,8 +169,9 @@ class MenuEntry {
result.addAll(MenuEntry.shortcuts(selection.menuChildren!));
} else {
if (selection.shortcut != null && selection.onPressed != null) {
result[selection.shortcut!] =
VoidCallbackIntent(selection.onPressed!);
result[selection.shortcut!] = VoidCallbackIntent(
selection.onPressed!,
);
}
}
}
@@ -221,7 +224,8 @@ class _MyCascadingContextMenuState extends State<_MyCascadingContextMenu> {
onPressed: () {
ContextMenuController.removeAny();
widget.onChangeSelection(
widget.showingMessage ? 'Hide Message' : 'Show Message');
widget.showingMessage ? 'Hide Message' : 'Show Message',
);
widget.onToggleMessageVisibility();
},
shortcut: const SingleActivator(LogicalKeyboardKey.keyS, control: true),
@@ -230,13 +234,14 @@ class _MyCascadingContextMenuState extends State<_MyCascadingContextMenu> {
// already hidden.
MenuEntry(
label: 'Reset',
onPressed: widget.showingMessage
? () {
ContextMenuController.removeAny();
widget.onChangeSelection('Reset');
widget.onToggleMessageVisibility();
}
: null,
onPressed:
widget.showingMessage
? () {
ContextMenuController.removeAny();
widget.onChangeSelection('Reset');
widget.onToggleMessageVisibility();
}
: null,
shortcut: const SingleActivator(LogicalKeyboardKey.escape),
),
MenuEntry(
@@ -249,8 +254,10 @@ class _MyCascadingContextMenuState extends State<_MyCascadingContextMenu> {
widget.onChangeSelection('Red Background');
widget.onChangeBackgroundColor(Colors.red);
},
shortcut:
const SingleActivator(LogicalKeyboardKey.keyR, control: true),
shortcut: const SingleActivator(
LogicalKeyboardKey.keyR,
control: true,
),
),
MenuEntry(
label: 'Green',
@@ -259,8 +266,10 @@ class _MyCascadingContextMenuState extends State<_MyCascadingContextMenu> {
widget.onChangeSelection('Green Background');
widget.onChangeBackgroundColor(Colors.green);
},
shortcut:
const SingleActivator(LogicalKeyboardKey.keyG, control: true),
shortcut: const SingleActivator(
LogicalKeyboardKey.keyG,
control: true,
),
),
MenuEntry(
label: 'Blue',
@@ -269,8 +278,10 @@ class _MyCascadingContextMenuState extends State<_MyCascadingContextMenu> {
widget.onChangeSelection('Blue Background');
widget.onChangeBackgroundColor(Colors.blue);
},
shortcut:
const SingleActivator(LogicalKeyboardKey.keyB, control: true),
shortcut: const SingleActivator(
LogicalKeyboardKey.keyB,
control: true,
),
),
],
),
@@ -278,8 +289,9 @@ class _MyCascadingContextMenuState extends State<_MyCascadingContextMenu> {
// (Re-)register the shortcuts with the ShortcutRegistry so that they are
// available to the entire application, and update them if they've changed.
_shortcutsEntry?.dispose();
_shortcutsEntry =
ShortcutRegistry.of(context).addAll(MenuEntry.shortcuts(result));
_shortcutsEntry = ShortcutRegistry.of(
context,
).addAll(MenuEntry.shortcuts(result));
return result;
}

View File

@@ -1,8 +1,8 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
typedef ContextMenuBuilder = Widget Function(
BuildContext context, Offset offset);
typedef ContextMenuBuilder =
Widget Function(BuildContext context, Offset offset);
/// Shows and hides the context menu based on user gestures.
///

View File

@@ -6,10 +6,7 @@ import 'constants.dart';
import 'platform_selector.dart';
class CustomButtonsPage extends StatelessWidget {
CustomButtonsPage({
super.key,
required this.onChangedPlatform,
});
CustomButtonsPage({super.key, required this.onChangedPlatform});
static const String route = 'custom-buttons';
static const String title = 'Custom Buttons';
@@ -31,9 +28,7 @@ class CustomButtonsPage extends StatelessWidget {
appBar: AppBar(
title: const Text(CustomButtonsPage.title),
actions: <Widget>[
PlatformSelector(
onChangedPlatform: onChangedPlatform,
),
PlatformSelector(onChangedPlatform: onChangedPlatform),
IconButton(
icon: const Icon(Icons.code),
onPressed: () async {
@@ -59,22 +54,24 @@ class CustomButtonsPage extends StatelessWidget {
// different buttons depending on the platform.
children:
editableTextState.contextMenuButtonItems.map((buttonItem) {
return CupertinoButton(
borderRadius: null,
color: const Color(0xffaaaa00),
disabledColor: const Color(0xffaaaaff),
onPressed: buttonItem.onPressed,
padding: const EdgeInsets.all(10.0),
pressedOpacity: 0.7,
child: SizedBox(
width: 200.0,
child: Text(
CupertinoTextSelectionToolbarButton.getButtonLabel(
context, buttonItem),
),
),
);
}).toList(),
return CupertinoButton(
borderRadius: null,
color: const Color(0xffaaaa00),
disabledColor: const Color(0xffaaaaff),
onPressed: buttonItem.onPressed,
padding: const EdgeInsets.all(10.0),
pressedOpacity: 0.7,
child: SizedBox(
width: 200.0,
child: Text(
CupertinoTextSelectionToolbarButton.getButtonLabel(
context,
buttonItem,
),
),
),
);
}).toList(),
);
},
),

View File

@@ -5,10 +5,7 @@ import 'constants.dart';
import 'platform_selector.dart';
class CustomMenuPage extends StatelessWidget {
CustomMenuPage({
super.key,
required this.onChangedPlatform,
});
CustomMenuPage({super.key, required this.onChangedPlatform});
static const String route = 'custom-menu';
static const String title = 'Custom Menu';
@@ -29,9 +26,7 @@ class CustomMenuPage extends StatelessWidget {
appBar: AppBar(
title: const Text(CustomMenuPage.title),
actions: <Widget>[
PlatformSelector(
onChangedPlatform: onChangedPlatform,
),
PlatformSelector(onChangedPlatform: onChangedPlatform),
IconButton(
icon: const Icon(Icons.code),
onPressed: () async {
@@ -52,10 +47,11 @@ class CustomMenuPage extends StatelessWidget {
contextMenuBuilder: (context, editableTextState) {
return _MyContextMenu(
anchor: editableTextState.contextMenuAnchors.primaryAnchor,
children: AdaptiveTextSelectionToolbar.getAdaptiveButtons(
context,
editableTextState.contextMenuButtonItems,
).toList(),
children:
AdaptiveTextSelectionToolbar.getAdaptiveButtons(
context,
editableTextState.contextMenuButtonItems,
).toList(),
);
},
),
@@ -66,10 +62,7 @@ class CustomMenuPage extends StatelessWidget {
}
class _MyContextMenu extends StatelessWidget {
const _MyContextMenu({
required this.anchor,
required this.children,
});
const _MyContextMenu({required this.anchor, required this.children});
final Offset anchor;
final List<Widget> children;
@@ -85,9 +78,7 @@ class _MyContextMenu extends StatelessWidget {
width: 200.0,
height: 200.0,
color: Colors.amberAccent,
child: Column(
children: children,
),
child: Column(children: children),
),
),
],

View File

@@ -5,10 +5,7 @@ import 'constants.dart';
import 'platform_selector.dart';
class DefaultValuesPage extends StatelessWidget {
DefaultValuesPage({
super.key,
required this.onChangedPlatform,
});
DefaultValuesPage({super.key, required this.onChangedPlatform});
static const String route = 'default-values';
static const String title = 'Default API Values Example';
@@ -44,9 +41,7 @@ class DefaultValuesPage extends StatelessWidget {
appBar: AppBar(
title: const Text(DefaultValuesPage.title),
actions: <Widget>[
PlatformSelector(
onChangedPlatform: onChangedPlatform,
),
PlatformSelector(onChangedPlatform: onChangedPlatform),
IconButton(
icon: const Icon(Icons.code),
onPressed: () async {
@@ -66,14 +61,8 @@ class DefaultValuesPage extends StatelessWidget {
const Text(
'This example simply shows what happens when contextMenuBuilder is given null, a custom value, or omitted altogether.',
),
const SizedBox(
height: 40.0,
),
TextField(
maxLines: 2,
minLines: 2,
controller: _controllerNone,
),
const SizedBox(height: 40.0),
TextField(maxLines: 2, minLines: 2, controller: _controllerNone),
TextField(
maxLines: 2,
minLines: 2,
@@ -92,8 +81,12 @@ class DefaultValuesPage extends StatelessWidget {
label: 'Custom button',
onPressed: () {
ContextMenuController.removeAny();
Navigator.of(context).push(_showDialog(
context, 'You clicked the custom button.'));
Navigator.of(context).push(
_showDialog(
context,
'You clicked the custom button.',
),
);
},
),
...editableTextState.contextMenuButtonItems,

View File

@@ -6,10 +6,7 @@ import 'is_valid_email.dart';
import 'platform_selector.dart';
class EmailButtonPage extends StatelessWidget {
EmailButtonPage({
super.key,
required this.onChangedPlatform,
});
EmailButtonPage({super.key, required this.onChangedPlatform});
static const String route = 'email-button';
static const String title = 'Email Button';
@@ -25,8 +22,9 @@ class EmailButtonPage extends StatelessWidget {
DialogRoute _showDialog(BuildContext context) {
return DialogRoute<void>(
context: context,
builder: (context) =>
const AlertDialog(title: Text('You clicked send email!')),
builder:
(context) =>
const AlertDialog(title: Text('You clicked send email!')),
);
}
@@ -36,9 +34,7 @@ class EmailButtonPage extends StatelessWidget {
appBar: AppBar(
title: const Text(EmailButtonPage.title),
actions: <Widget>[
PlatformSelector(
onChangedPlatform: onChangedPlatform,
),
PlatformSelector(onChangedPlatform: onChangedPlatform),
IconButton(
icon: const Icon(Icons.code),
onPressed: () async {
@@ -70,14 +66,15 @@ class EmailButtonPage extends StatelessWidget {
editableTextState.contextMenuButtonItems;
if (isValidEmail(value.selection.textInside(value.text))) {
buttonItems.insert(
0,
ContextMenuButtonItem(
label: 'Send email',
onPressed: () {
ContextMenuController.removeAny();
Navigator.of(context).push(_showDialog(context));
},
));
0,
ContextMenuButtonItem(
label: 'Send email',
onPressed: () {
ContextMenuController.removeAny();
Navigator.of(context).push(_showDialog(context));
},
),
);
}
return AdaptiveTextSelectionToolbar.buttonItems(
anchors: editableTextState.contextMenuAnchors,

View File

@@ -6,10 +6,7 @@ import 'constants.dart';
import 'platform_selector.dart';
class FieldTypesPage extends StatelessWidget {
FieldTypesPage({
super.key,
required this.onChangedPlatform,
});
FieldTypesPage({super.key, required this.onChangedPlatform});
static const String route = 'field-types';
static const String title = 'The Context Menu in Different Field Types';
@@ -36,8 +33,9 @@ class FieldTypesPage extends StatelessWidget {
final TextEditingController _cupertinoControllerForced =
TextEditingController(
text: 'Or forced to always show a specific menu (Material desktop menu).',
);
text:
'Or forced to always show a specific menu (Material desktop menu).',
);
final TextEditingController _editableController = TextEditingController(
text:
@@ -50,9 +48,7 @@ class FieldTypesPage extends StatelessWidget {
appBar: AppBar(
title: const Text(FieldTypesPage.title),
actions: <Widget>[
PlatformSelector(
onChangedPlatform: onChangedPlatform,
),
PlatformSelector(onChangedPlatform: onChangedPlatform),
IconButton(
icon: const Icon(Icons.code),
onPressed: () async {
@@ -69,15 +65,9 @@ class FieldTypesPage extends StatelessWidget {
child: ListView(
children: <Widget>[
const SizedBox(height: 20.0),
TextField(
maxLines: 3,
controller: _controller,
),
TextField(maxLines: 3, controller: _controller),
const SizedBox(height: 60.0),
CupertinoTextField(
maxLines: 3,
controller: _cupertinoController,
),
CupertinoTextField(maxLines: 3, controller: _cupertinoController),
const SizedBox(height: 20.0),
CupertinoTextField(
maxLines: 3,
@@ -95,10 +85,11 @@ class FieldTypesPage extends StatelessWidget {
contextMenuBuilder: (context, editableTextState) {
return DesktopTextSelectionToolbar(
anchor: editableTextState.contextMenuAnchors.primaryAnchor,
children: AdaptiveTextSelectionToolbar.getAdaptiveButtons(
context,
editableTextState.contextMenuButtonItems,
).toList(),
children:
AdaptiveTextSelectionToolbar.getAdaptiveButtons(
context,
editableTextState.contextMenuButtonItems,
).toList(),
);
},
),

View File

@@ -8,10 +8,7 @@ import 'is_valid_email.dart';
import 'platform_selector.dart';
class FullPage extends StatelessWidget {
FullPage({
super.key,
required this.onChangedPlatform,
});
FullPage({super.key, required this.onChangedPlatform});
static const String route = 'full';
static const String title = 'Combined Example';
@@ -38,9 +35,7 @@ class FullPage extends StatelessWidget {
appBar: AppBar(
title: const Text(FullPage.title),
actions: <Widget>[
PlatformSelector(
onChangedPlatform: onChangedPlatform,
),
PlatformSelector(onChangedPlatform: onChangedPlatform),
IconButton(
icon: const Icon(Icons.code),
onPressed: () async {
@@ -54,9 +49,7 @@ class FullPage extends StatelessWidget {
body: ContextMenuRegion(
contextMenuBuilder: (context, offset) {
return AdaptiveTextSelectionToolbar.buttonItems(
anchors: TextSelectionToolbarAnchors(
primaryAnchor: offset,
),
anchors: TextSelectionToolbarAnchors(primaryAnchor: offset),
buttonItems: <ContextMenuButtonItem>[
ContextMenuButtonItem(
onPressed: () {
@@ -77,9 +70,7 @@ class FullPage extends StatelessWidget {
const Text(
'This example simply shows how many of the previous examples can be combined in a single app.',
),
const SizedBox(
height: 60.0,
),
const SizedBox(height: 60.0),
ContextMenuRegion(
contextMenuBuilder: (context, offset) {
return AdaptiveTextSelectionToolbar.buttonItems(
@@ -90,8 +81,12 @@ class FullPage extends StatelessWidget {
ContextMenuButtonItem(
onPressed: () {
ContextMenuController.removeAny();
Navigator.of(context).push(_showDialog(
context, 'Image saved! (not really though)'));
Navigator.of(context).push(
_showDialog(
context,
'Image saved! (not really though)',
),
);
},
label: 'Save',
),
@@ -114,38 +109,43 @@ class FullPage extends StatelessWidget {
editableTextState.contextMenuButtonItems;
if (isValidEmail(value.selection.textInside(value.text))) {
buttonItems.insert(
0,
ContextMenuButtonItem(
label: 'Send email',
onPressed: () {
ContextMenuController.removeAny();
Navigator.of(context).push(_showDialog(
context, 'You clicked send email'));
},
));
0,
ContextMenuButtonItem(
label: 'Send email',
onPressed: () {
ContextMenuController.removeAny();
Navigator.of(context).push(
_showDialog(context, 'You clicked send email'),
);
},
),
);
}
return AdaptiveTextSelectionToolbar(
anchors: editableTextState.contextMenuAnchors,
// Build the default buttons, but make them look crazy.
// Note that in a real project you may want to build
// different buttons depending on the platform.
children: buttonItems.map((buttonItem) {
return CupertinoButton(
borderRadius: null,
color: const Color(0xffaaaa00),
disabledColor: const Color(0xffaaaaff),
onPressed: buttonItem.onPressed,
padding: const EdgeInsets.all(10.0),
pressedOpacity: 0.7,
child: SizedBox(
width: 200.0,
child: Text(
CupertinoTextSelectionToolbarButton
.getButtonLabel(context, buttonItem),
),
),
);
}).toList(),
children:
buttonItems.map((buttonItem) {
return CupertinoButton(
borderRadius: null,
color: const Color(0xffaaaa00),
disabledColor: const Color(0xffaaaaff),
onPressed: buttonItem.onPressed,
padding: const EdgeInsets.all(10.0),
pressedOpacity: 0.7,
child: SizedBox(
width: 200.0,
child: Text(
CupertinoTextSelectionToolbarButton.getButtonLabel(
context,
buttonItem,
),
),
),
);
}).toList(),
);
},
),

View File

@@ -5,10 +5,7 @@ import 'constants.dart';
import 'platform_selector.dart';
class GlobalSelectionPage extends StatelessWidget {
GlobalSelectionPage({
super.key,
required this.onChangedPlatform,
});
GlobalSelectionPage({super.key, required this.onChangedPlatform});
static const String route = 'global-selection';
static const String title = 'Global Selection Example';
@@ -43,9 +40,7 @@ class GlobalSelectionPage extends StatelessWidget {
appBar: AppBar(
title: const Text(GlobalSelectionPage.title),
actions: <Widget>[
PlatformSelector(
onChangedPlatform: onChangedPlatform,
),
PlatformSelector(onChangedPlatform: onChangedPlatform),
IconButton(
icon: const Icon(Icons.code),
onPressed: () async {
@@ -69,7 +64,8 @@ class GlobalSelectionPage extends StatelessWidget {
TextField(controller: _controller),
const SizedBox(height: 40.0),
const SelectableText(
'SelectableText also shows its own separate context menu.'),
'SelectableText also shows its own separate context menu.',
),
],
),
),

View File

@@ -6,10 +6,7 @@ import 'context_menu_region.dart';
import 'platform_selector.dart';
class ImagePage extends StatelessWidget {
const ImagePage({
super.key,
required this.onChangedPlatform,
});
const ImagePage({super.key, required this.onChangedPlatform});
static const String route = 'image';
static const String title = 'ContextMenu on an Image';
@@ -22,8 +19,10 @@ class ImagePage extends StatelessWidget {
DialogRoute _showDialog(BuildContext context) {
return DialogRoute<void>(
context: context,
builder: (context) =>
const AlertDialog(title: Text('Image saved! (not really though)')),
builder:
(context) => const AlertDialog(
title: Text('Image saved! (not really though)'),
),
);
}
@@ -33,9 +32,7 @@ class ImagePage extends StatelessWidget {
appBar: AppBar(
title: const Text(ImagePage.title),
actions: <Widget>[
PlatformSelector(
onChangedPlatform: onChangedPlatform,
),
PlatformSelector(onChangedPlatform: onChangedPlatform),
IconButton(
icon: const Icon(Icons.code),
onPressed: () async {
@@ -52,9 +49,7 @@ class ImagePage extends StatelessWidget {
ContextMenuRegion(
contextMenuBuilder: (context, offset) {
return AdaptiveTextSelectionToolbar.buttonItems(
anchors: TextSelectionToolbarAnchors(
primaryAnchor: offset,
),
anchors: TextSelectionToolbarAnchors(primaryAnchor: offset),
buttonItems: <ContextMenuButtonItem>[
ContextMenuButtonItem(
onPressed: () {

View File

@@ -21,9 +21,7 @@ void main() {
}
class MyApp extends StatefulWidget {
const MyApp({
super.key,
});
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
@@ -66,40 +64,43 @@ class _MyAppState extends State<MyApp> {
initialRoute: '/',
routes: <String, Widget Function(BuildContext)>{
'/': (context) => MyHomePage(onChangedPlatform: onChangedPlatform),
AnywherePage.route: (context) =>
AnywherePage(onChangedPlatform: onChangedPlatform),
CustomButtonsPage.route: (context) =>
CustomButtonsPage(onChangedPlatform: onChangedPlatform),
CustomMenuPage.route: (context) =>
CustomMenuPage(onChangedPlatform: onChangedPlatform),
ReorderedButtonsPage.route: (context) =>
ReorderedButtonsPage(onChangedPlatform: onChangedPlatform),
EmailButtonPage.route: (context) =>
EmailButtonPage(onChangedPlatform: onChangedPlatform),
ImagePage.route: (context) =>
ImagePage(onChangedPlatform: onChangedPlatform),
FieldTypesPage.route: (context) =>
FieldTypesPage(onChangedPlatform: onChangedPlatform),
FullPage.route: (context) =>
FullPage(onChangedPlatform: onChangedPlatform),
ModifiedActionPage.route: (context) =>
ModifiedActionPage(onChangedPlatform: onChangedPlatform),
GlobalSelectionPage.route: (context) =>
GlobalSelectionPage(onChangedPlatform: onChangedPlatform),
DefaultValuesPage.route: (context) =>
DefaultValuesPage(onChangedPlatform: onChangedPlatform),
CascadingMenuPage.route: (context) =>
CascadingMenuPage(onChangedPlatform: onChangedPlatform),
AnywherePage.route:
(context) => AnywherePage(onChangedPlatform: onChangedPlatform),
CustomButtonsPage.route:
(context) =>
CustomButtonsPage(onChangedPlatform: onChangedPlatform),
CustomMenuPage.route:
(context) => CustomMenuPage(onChangedPlatform: onChangedPlatform),
ReorderedButtonsPage.route:
(context) =>
ReorderedButtonsPage(onChangedPlatform: onChangedPlatform),
EmailButtonPage.route:
(context) => EmailButtonPage(onChangedPlatform: onChangedPlatform),
ImagePage.route:
(context) => ImagePage(onChangedPlatform: onChangedPlatform),
FieldTypesPage.route:
(context) => FieldTypesPage(onChangedPlatform: onChangedPlatform),
FullPage.route:
(context) => FullPage(onChangedPlatform: onChangedPlatform),
ModifiedActionPage.route:
(context) =>
ModifiedActionPage(onChangedPlatform: onChangedPlatform),
GlobalSelectionPage.route:
(context) =>
GlobalSelectionPage(onChangedPlatform: onChangedPlatform),
DefaultValuesPage.route:
(context) =>
DefaultValuesPage(onChangedPlatform: onChangedPlatform),
CascadingMenuPage.route:
(context) =>
CascadingMenuPage(onChangedPlatform: onChangedPlatform),
},
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({
super.key,
required this.onChangedPlatform,
});
const MyHomePage({super.key, required this.onChangedPlatform});
final PlatformCallback onChangedPlatform;
@@ -109,9 +110,7 @@ class MyHomePage extends StatelessWidget {
appBar: AppBar(
title: const Text('Context Menu Demos'),
actions: <Widget>[
PlatformSelector(
onChangedPlatform: onChangedPlatform,
),
PlatformSelector(onChangedPlatform: onChangedPlatform),
],
),
body: ListView(
@@ -203,10 +202,7 @@ class _MyListItem extends StatelessWidget {
margin: const EdgeInsets.all(12.0),
child: Padding(
padding: const EdgeInsets.all(24.0),
child: ListTile(
title: Text(title),
subtitle: Text(subtitle),
),
child: ListTile(title: Text(title), subtitle: Text(subtitle)),
),
),
);

View File

@@ -5,10 +5,7 @@ import 'constants.dart';
import 'platform_selector.dart';
class ModifiedActionPage extends StatelessWidget {
ModifiedActionPage({
super.key,
required this.onChangedPlatform,
});
ModifiedActionPage({super.key, required this.onChangedPlatform});
static const String route = 'modified-action';
static const String title = 'Modified Action';
@@ -25,8 +22,10 @@ class ModifiedActionPage extends StatelessWidget {
DialogRoute _showDialog(BuildContext context) {
return DialogRoute<void>(
context: context,
builder: (context) => const AlertDialog(
title: Text('Copied, but also showed this dialog.')),
builder:
(context) => const AlertDialog(
title: Text('Copied, but also showed this dialog.'),
),
);
}
@@ -36,9 +35,7 @@ class ModifiedActionPage extends StatelessWidget {
appBar: AppBar(
title: const Text(ModifiedActionPage.title),
actions: <Widget>[
PlatformSelector(
onChangedPlatform: onChangedPlatform,
),
PlatformSelector(onChangedPlatform: onChangedPlatform),
IconButton(
icon: const Icon(Icons.code),
onPressed: () async {
@@ -58,20 +55,18 @@ class ModifiedActionPage extends StatelessWidget {
const Text(
'This example shows adding to the behavior of a default button.',
),
const SizedBox(
height: 30.0,
),
const SizedBox(height: 30.0),
TextField(
controller: _controller,
contextMenuBuilder: (context, editableTextState) {
final List<ContextMenuButtonItem> buttonItems =
editableTextState.contextMenuButtonItems;
// Modify the copy buttonItem to show a dialog after copying.
final int copyButtonIndex = buttonItems.indexWhere(
(buttonItem) {
return buttonItem.type == ContextMenuButtonType.copy;
},
);
final int copyButtonIndex = buttonItems.indexWhere((
buttonItem,
) {
return buttonItem.type == ContextMenuButtonType.copy;
});
if (copyButtonIndex >= 0) {
final ContextMenuButtonItem copyButtonItem =
buttonItems[copyButtonIndex];

View File

@@ -4,10 +4,7 @@ import 'package:flutter/material.dart';
typedef PlatformCallback = void Function(TargetPlatform platform);
class PlatformSelector extends StatefulWidget {
const PlatformSelector({
super.key,
required this.onChangedPlatform,
});
const PlatformSelector({super.key, required this.onChangedPlatform});
final PlatformCallback onChangedPlatform;
@@ -40,21 +37,19 @@ class _PlatformSelectorState extends State<PlatformSelector> {
widget.onChangedPlatform(value);
setState(() {});
},
items: TargetPlatform.values.map((platform) {
return DropdownMenuItem<TargetPlatform>(
value: platform,
child: Row(
children: <Widget>[
if (platform == originaPlatform)
const Icon(
Icons.home,
color: Color(0xff616161),
),
Text(_platformToString(platform)),
],
),
);
}).toList(),
items:
TargetPlatform.values.map((platform) {
return DropdownMenuItem<TargetPlatform>(
value: platform,
child: Row(
children: <Widget>[
if (platform == originaPlatform)
const Icon(Icons.home, color: Color(0xff616161)),
Text(_platformToString(platform)),
],
),
);
}).toList(),
),
);
}

View File

@@ -7,10 +7,7 @@ import 'constants.dart';
import 'platform_selector.dart';
class ReorderedButtonsPage extends StatelessWidget {
ReorderedButtonsPage({
super.key,
required this.onChangedPlatform,
});
ReorderedButtonsPage({super.key, required this.onChangedPlatform});
static const String route = 'reordered-buttons';
static const String title = 'Reordered Buttons';
@@ -33,9 +30,7 @@ class ReorderedButtonsPage extends StatelessWidget {
appBar: AppBar(
title: const Text(ReorderedButtonsPage.title),
actions: <Widget>[
PlatformSelector(
onChangedPlatform: onChangedPlatform,
),
PlatformSelector(onChangedPlatform: onChangedPlatform),
IconButton(
icon: const Icon(Icons.code),
onPressed: () async {
@@ -52,10 +47,7 @@ class ReorderedButtonsPage extends StatelessWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
maxLines: 2,
controller: _controllerNormal,
),
TextField(maxLines: 2, controller: _controllerNormal),
const SizedBox(height: 20.0),
TextField(
controller: _controllerReordered,
@@ -63,16 +55,17 @@ class ReorderedButtonsPage extends StatelessWidget {
contextMenuBuilder: (context, editableTextState) {
// Reorder the button datas by type.
final HashMap<ContextMenuButtonType, ContextMenuButtonItem>
buttonItemsMap =
buttonItemsMap =
HashMap<ContextMenuButtonType, ContextMenuButtonItem>();
for (ContextMenuButtonItem buttonItem
in editableTextState.contextMenuButtonItems) {
buttonItemsMap[buttonItem.type] = buttonItem;
}
final List<ContextMenuButtonItem> reorderedButtonItems =
<ContextMenuButtonItem>[
if (buttonItemsMap
.containsKey(ContextMenuButtonType.selectAll))
final List<ContextMenuButtonItem>
reorderedButtonItems = <ContextMenuButtonItem>[
if (buttonItemsMap.containsKey(
ContextMenuButtonType.selectAll,
))
buttonItemsMap[ContextMenuButtonType.selectAll]!,
if (buttonItemsMap.containsKey(ContextMenuButtonType.paste))
buttonItemsMap[ContextMenuButtonType.paste]!,

View File

@@ -20,7 +20,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: ^3.5.0
sdk: ^3.7.0-0
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions

View File

@@ -6,121 +6,124 @@ import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Can show and use the cascading menu', (tester) async {
await tester.pumpWidget(const MyApp());
testWidgets(
'Can show and use the cascading menu',
(tester) async {
await tester.pumpWidget(const MyApp());
// Navigate to the CascadingMenuPage example.
await tester.dragUntilVisible(
find.text(CascadingMenuPage.title),
find.byType(ListView),
const Offset(0.0, -250.0),
);
await tester.pumpAndSettle();
await tester.tap(find.text(CascadingMenuPage.title));
await tester.pumpAndSettle();
// Navigate to the CascadingMenuPage example.
await tester.dragUntilVisible(
find.text(CascadingMenuPage.title),
find.byType(ListView),
const Offset(0.0, -250.0),
);
await tester.pumpAndSettle();
await tester.tap(find.text(CascadingMenuPage.title));
await tester.pumpAndSettle();
const String message =
'Right click or long press anywhere to show the cascading menu.';
expect(find.text(message), findsOneWidget);
const String message =
'Right click or long press anywhere to show the cascading menu.';
expect(find.text(message), findsOneWidget);
// Right click on the background of the app to show the cascading context
// menu.
TestGesture gesture = await tester.startGesture(
const Offset(0.0, 100.0),
kind: PointerDeviceKind.mouse,
buttons: kSecondaryMouseButton,
);
await tester.pump();
await gesture.up();
await gesture.removePointer();
await tester.pumpAndSettle();
// Right click on the background of the app to show the cascading context
// menu.
TestGesture gesture = await tester.startGesture(
const Offset(0.0, 100.0),
kind: PointerDeviceKind.mouse,
buttons: kSecondaryMouseButton,
);
await tester.pump();
await gesture.up();
await gesture.removePointer();
await tester.pumpAndSettle();
// The custom cascading context menu is shown.
expect(find.byType(DesktopTextSelectionToolbar), findsOneWidget);
expect(find.text('About'), findsOneWidget);
expect(find.text('Show'), findsNothing);
expect(find.text('Hide'), findsOneWidget);
expect(find.text('Reset'), findsOneWidget);
expect(find.text('Color'), findsOneWidget);
// The custom cascading context menu is shown.
expect(find.byType(DesktopTextSelectionToolbar), findsOneWidget);
expect(find.text('About'), findsOneWidget);
expect(find.text('Show'), findsNothing);
expect(find.text('Hide'), findsOneWidget);
expect(find.text('Reset'), findsOneWidget);
expect(find.text('Color'), findsOneWidget);
// Click "Hide Message" and the message and menu are hidden.
await tester.tap(find.text('Hide'));
await tester.pumpAndSettle();
expect(find.byType(DesktopTextSelectionToolbar), findsNothing);
expect(find.text(message), findsNothing);
// Click "Hide Message" and the message and menu are hidden.
await tester.tap(find.text('Hide'));
await tester.pumpAndSettle();
expect(find.byType(DesktopTextSelectionToolbar), findsNothing);
expect(find.text(message), findsNothing);
// Right click to show the menu again.
gesture = await tester.startGesture(
const Offset(0.0, 100.0),
kind: PointerDeviceKind.mouse,
buttons: kSecondaryMouseButton,
);
await tester.pump();
await gesture.up();
await gesture.removePointer();
await tester.pumpAndSettle();
expect(find.byType(DesktopTextSelectionToolbar), findsOneWidget);
expect(find.text('About'), findsOneWidget);
expect(find.text('Show'), findsOneWidget);
expect(find.text('Hide'), findsNothing);
expect(find.text('Reset'), findsOneWidget);
expect(find.text('Color'), findsOneWidget);
// Right click to show the menu again.
gesture = await tester.startGesture(
const Offset(0.0, 100.0),
kind: PointerDeviceKind.mouse,
buttons: kSecondaryMouseButton,
);
await tester.pump();
await gesture.up();
await gesture.removePointer();
await tester.pumpAndSettle();
expect(find.byType(DesktopTextSelectionToolbar), findsOneWidget);
expect(find.text('About'), findsOneWidget);
expect(find.text('Show'), findsOneWidget);
expect(find.text('Hide'), findsNothing);
expect(find.text('Reset'), findsOneWidget);
expect(find.text('Color'), findsOneWidget);
// Use the shortcut to show the message again.
await tester.sendKeyDownEvent(LogicalKeyboardKey.control);
await tester.sendKeyDownEvent(LogicalKeyboardKey.keyS);
await tester.sendKeyUpEvent(LogicalKeyboardKey.control);
await tester.sendKeyUpEvent(LogicalKeyboardKey.keyS);
await tester.pumpAndSettle();
expect(find.byType(DesktopTextSelectionToolbar), findsNothing);
expect(find.text(message), findsOneWidget);
// Use the shortcut to show the message again.
await tester.sendKeyDownEvent(LogicalKeyboardKey.control);
await tester.sendKeyDownEvent(LogicalKeyboardKey.keyS);
await tester.sendKeyUpEvent(LogicalKeyboardKey.control);
await tester.sendKeyUpEvent(LogicalKeyboardKey.keyS);
await tester.pumpAndSettle();
expect(find.byType(DesktopTextSelectionToolbar), findsNothing);
expect(find.text(message), findsOneWidget);
// Right click to show the menu again.
gesture = await tester.startGesture(
const Offset(0.0, 100.0),
kind: PointerDeviceKind.mouse,
buttons: kSecondaryMouseButton,
);
await tester.pump();
await gesture.up();
await gesture.removePointer();
await tester.pumpAndSettle();
expect(find.byType(DesktopTextSelectionToolbar), findsOneWidget);
expect(find.text('About'), findsOneWidget);
expect(find.text('Show'), findsNothing);
expect(find.text('Hide'), findsOneWidget);
expect(find.text('Reset'), findsOneWidget);
expect(find.text('Color'), findsOneWidget);
expect(find.text('Red'), findsNothing);
expect(find.text('Green'), findsNothing);
expect(find.text('Blue'), findsNothing);
// Right click to show the menu again.
gesture = await tester.startGesture(
const Offset(0.0, 100.0),
kind: PointerDeviceKind.mouse,
buttons: kSecondaryMouseButton,
);
await tester.pump();
await gesture.up();
await gesture.removePointer();
await tester.pumpAndSettle();
expect(find.byType(DesktopTextSelectionToolbar), findsOneWidget);
expect(find.text('About'), findsOneWidget);
expect(find.text('Show'), findsNothing);
expect(find.text('Hide'), findsOneWidget);
expect(find.text('Reset'), findsOneWidget);
expect(find.text('Color'), findsOneWidget);
expect(find.text('Red'), findsNothing);
expect(find.text('Green'), findsNothing);
expect(find.text('Blue'), findsNothing);
// Hover Background Color.
gesture = await tester.startGesture(
//tester.getCenter(find.text('Color')),
const Offset(0.0, 100),
kind: PointerDeviceKind.mouse,
);
await tester.pump();
await gesture.moveTo(tester.getCenter(find.text('Color')));
await tester.pumpAndSettle();
// Hover Background Color.
gesture = await tester.startGesture(
//tester.getCenter(find.text('Color')),
const Offset(0.0, 100),
kind: PointerDeviceKind.mouse,
);
await tester.pump();
await gesture.moveTo(tester.getCenter(find.text('Color')));
await tester.pumpAndSettle();
// Background colors are shown.
expect(find.text('Red'), findsOneWidget);
expect(find.text('Green'), findsOneWidget);
expect(find.text('Blue'), findsOneWidget);
// Background colors are shown.
expect(find.text('Red'), findsOneWidget);
expect(find.text('Green'), findsOneWidget);
expect(find.text('Blue'), findsOneWidget);
// Select green.
await gesture.moveTo(tester.getCenter(find.text('Green')));
await tester.pump();
await tester.tap(find.text('Green'));
await tester.pump();
await gesture.removePointer();
// Select green.
await gesture.moveTo(tester.getCenter(find.text('Green')));
await tester.pump();
await tester.tap(find.text('Green'));
await tester.pump();
await gesture.removePointer();
// The menu is gone and the UI shows green was selected.
expect(find.byType(DesktopTextSelectionToolbar), findsNothing);
expect(find.text('Last Selected: Green Background'), findsOneWidget);
},
// TODO(justinmc): https://github.com/flutter/samples/issues/2086
skip: true);
// The menu is gone and the UI shows green was selected.
expect(find.byType(DesktopTextSelectionToolbar), findsNothing);
expect(find.text('Last Selected: Green Background'), findsOneWidget);
},
// TODO(justinmc): https://github.com/flutter/samples/issues/2086
skip: true,
);
}

View File

@@ -7,8 +7,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Shows custom buttons in the built-in context menu',
(tester) async {
testWidgets('Shows custom buttons in the built-in context menu', (
tester,
) async {
await tester.pumpWidget(const MyApp());
// Navigate to the CustomButtonsPage example.
@@ -40,8 +41,10 @@ void main() {
expect(find.byType(CupertinoButton), findsNWidgets(2));
case TargetPlatform.macOS:
expect(find.byType(CupertinoButton), findsNWidgets(2));
expect(find.byType(CupertinoDesktopTextSelectionToolbarButton),
findsNothing);
expect(
find.byType(CupertinoDesktopTextSelectionToolbarButton),
findsNothing,
);
case TargetPlatform.android:
case TargetPlatform.fuchsia:
expect(find.byType(CupertinoButton), findsNWidgets(1));

View File

@@ -37,17 +37,23 @@ void main() {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
expect(
find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(2));
find.byType(CupertinoTextSelectionToolbarButton),
findsNWidgets(2),
);
case TargetPlatform.macOS:
expect(find.byType(CupertinoDesktopTextSelectionToolbarButton),
findsNWidgets(2));
expect(
find.byType(CupertinoDesktopTextSelectionToolbarButton),
findsNWidgets(2),
);
case TargetPlatform.android:
case TargetPlatform.fuchsia:
expect(find.byType(TextSelectionToolbarTextButton), findsNWidgets(1));
case TargetPlatform.linux:
case TargetPlatform.windows:
expect(
find.byType(DesktopTextSelectionToolbarButton), findsNWidgets(1));
find.byType(DesktopTextSelectionToolbarButton),
findsNWidgets(1),
);
}
});
}

View File

@@ -7,8 +7,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Gives correct behavior for all values of contextMenuBuilder',
(tester) async {
testWidgets('Gives correct behavior for all values of contextMenuBuilder', (
tester,
) async {
await tester.pumpWidget(const MyApp());
// Navigate to the DefaultValuesPage example.
@@ -44,17 +45,23 @@ void main() {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
expect(
find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(2));
find.byType(CupertinoTextSelectionToolbarButton),
findsNWidgets(2),
);
case TargetPlatform.macOS:
expect(find.byType(CupertinoDesktopTextSelectionToolbarButton),
findsNWidgets(2));
expect(
find.byType(CupertinoDesktopTextSelectionToolbarButton),
findsNWidgets(2),
);
case TargetPlatform.android:
case TargetPlatform.fuchsia:
expect(find.byType(TextSelectionToolbarTextButton), findsNWidgets(1));
case TargetPlatform.linux:
case TargetPlatform.windows:
expect(
find.byType(DesktopTextSelectionToolbarButton), findsNWidgets(1));
find.byType(DesktopTextSelectionToolbarButton),
findsNWidgets(1),
);
}
// Tap the next field to hide the context menu.

View File

@@ -10,8 +10,9 @@ import 'package:flutter_test/flutter_test.dart';
import 'utils.dart';
void main() {
testWidgets('Selecting the email address shows a custom button',
(tester) async {
testWidgets('Selecting the email address shows a custom button', (
tester,
) async {
await tester.pumpWidget(const MyApp());
// Navigate to the EmailButtonPage example.
@@ -50,17 +51,23 @@ void main() {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
expect(
find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(2));
find.byType(CupertinoTextSelectionToolbarButton),
findsNWidgets(2),
);
case TargetPlatform.macOS:
expect(find.byType(CupertinoDesktopTextSelectionToolbarButton),
findsNWidgets(2));
expect(
find.byType(CupertinoDesktopTextSelectionToolbarButton),
findsNWidgets(2),
);
case TargetPlatform.android:
case TargetPlatform.fuchsia:
expect(find.byType(TextSelectionToolbarTextButton), findsNWidgets(3));
case TargetPlatform.linux:
case TargetPlatform.windows:
expect(
find.byType(DesktopTextSelectionToolbarButton), findsNWidgets(3));
find.byType(DesktopTextSelectionToolbarButton),
findsNWidgets(3),
);
}
// Click on "Copy" to hide the context menu.
@@ -94,17 +101,23 @@ void main() {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
expect(
find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(3));
find.byType(CupertinoTextSelectionToolbarButton),
findsNWidgets(3),
);
case TargetPlatform.macOS:
expect(find.byType(CupertinoDesktopTextSelectionToolbarButton),
findsNWidgets(3));
expect(
find.byType(CupertinoDesktopTextSelectionToolbarButton),
findsNWidgets(3),
);
case TargetPlatform.android:
case TargetPlatform.fuchsia:
expect(find.byType(TextSelectionToolbarTextButton), findsNWidgets(4));
case TargetPlatform.linux:
case TargetPlatform.windows:
expect(
find.byType(DesktopTextSelectionToolbarButton), findsNWidgets(4));
find.byType(DesktopTextSelectionToolbarButton),
findsNWidgets(4),
);
}
// TODO: Test failing on Flutter 3.18.0-7.0.pre.57 https://github.com/flutter/samples/issues/2110
}, skip: true);

View File

@@ -52,8 +52,10 @@ void main() {
case TargetPlatform.windows:
expect(find.byType(DesktopTextSelectionToolbar), findsOneWidget);
case TargetPlatform.macOS:
expect(find.byType(CupertinoDesktopTextSelectionToolbar),
findsOneWidget);
expect(
find.byType(CupertinoDesktopTextSelectionToolbar),
findsOneWidget,
);
}
// Tap the next field to hide the context menu.
@@ -74,7 +76,9 @@ void main() {
// The default Cupertino context menu is shown.
expect(
find.byType(CupertinoAdaptiveTextSelectionToolbar), findsOneWidget);
find.byType(CupertinoAdaptiveTextSelectionToolbar),
findsOneWidget,
);
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
case TargetPlatform.android:
@@ -83,8 +87,10 @@ void main() {
case TargetPlatform.macOS:
case TargetPlatform.linux:
case TargetPlatform.windows:
expect(find.byType(CupertinoDesktopTextSelectionToolbar),
findsOneWidget);
expect(
find.byType(CupertinoDesktopTextSelectionToolbar),
findsOneWidget,
);
}
// Tap the next field to hide the context menu.
@@ -115,8 +121,10 @@ void main() {
case TargetPlatform.windows:
expect(find.byType(DesktopTextSelectionToolbar), findsOneWidget);
case TargetPlatform.macOS:
expect(find.byType(CupertinoDesktopTextSelectionToolbar),
findsOneWidget);
expect(
find.byType(CupertinoDesktopTextSelectionToolbar),
findsOneWidget,
);
}
// Tap the next field to hide the context menu.
@@ -168,8 +176,10 @@ void main() {
case TargetPlatform.windows:
expect(find.byType(DesktopTextSelectionToolbar), findsOneWidget);
case TargetPlatform.macOS:
expect(find.byType(CupertinoDesktopTextSelectionToolbar),
findsOneWidget);
expect(
find.byType(CupertinoDesktopTextSelectionToolbar),
findsOneWidget,
);
}
},
variant: TargetPlatformVariant.all(),

View File

@@ -5,8 +5,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Gives correct behavior for all values of contextMenuBuilder',
(tester) async {
testWidgets('Gives correct behavior for all values of contextMenuBuilder', (
tester,
) async {
await tester.pumpWidget(const MyApp());
// Navigate to the GlobalSelectionPage example.
@@ -28,10 +29,9 @@ void main() {
// Right click on the plain Text widget.
TestGesture gesture = await tester.startGesture(
tester.getCenter(find.descendant(
of: find.byType(ListView),
matching: find.byType(Text),
)),
tester.getCenter(
find.descendant(of: find.byType(ListView), matching: find.byType(Text)),
),
kind: PointerDeviceKind.mouse,
buttons: kSecondaryMouseButton,
);

View File

@@ -36,11 +36,10 @@ Offset textOffsetToPosition(WidgetTester tester, int offset) {
}
List<TextSelectionPoint> globalize(
Iterable<TextSelectionPoint> points, RenderBox box) {
Iterable<TextSelectionPoint> points,
RenderBox box,
) {
return points.map<TextSelectionPoint>((point) {
return TextSelectionPoint(
box.localToGlobal(point.point),
point.direction,
);
return TextSelectionPoint(box.localToGlobal(point.point), point.direction);
}).toList();
}