diff --git a/examples/api/lib/material/floating_action_button/floating_action_button.0.dart b/examples/api/lib/material/floating_action_button/floating_action_button.0.dart index 26b2a5baf8..30a0b3295f 100644 --- a/examples/api/lib/material/floating_action_button/floating_action_button.0.dart +++ b/examples/api/lib/material/floating_action_button/floating_action_button.0.dart @@ -6,21 +6,38 @@ import 'package:flutter/material.dart'; /// Flutter code sample for [FloatingActionButton]. -void main() => runApp(const FloatingActionButtonExampleApp()); +void main() { + runApp(const FloatingActionButtonExampleApp()); +} class FloatingActionButtonExampleApp extends StatelessWidget { - const FloatingActionButtonExampleApp({super.key}); + const FloatingActionButtonExampleApp({ super.key }); @override Widget build(BuildContext context) { - return const MaterialApp( - home: FabExample(), + return MaterialApp( + theme: ThemeData(useMaterial3: true), + home: const FloatingActionButtonExample(), ); } } -class FabExample extends StatelessWidget { - const FabExample({super.key}); +class FloatingActionButtonExample extends StatefulWidget { + const FloatingActionButtonExample({ super.key }); + + @override + State createState() => _FloatingActionButtonExampleState(); +} + +class _FloatingActionButtonExampleState extends State { + // The FAB's foregroundColor, backgroundColor, and shape + static const List<(Color?, Color? background, ShapeBorder?)> customizations = <(Color?, Color?, ShapeBorder?)>[ + (null, null, null), // The FAB uses its default for null parameters. + (null, Colors.green, null), + (Colors.white, Colors.green, null), + (Colors.white, Colors.green, CircleBorder()), + ]; + int index = 0; // Selects the customization. @override Widget build(BuildContext context) { @@ -31,9 +48,13 @@ class FabExample extends StatelessWidget { body: const Center(child: Text('Press the button below!')), floatingActionButton: FloatingActionButton( onPressed: () { - // Add your onPressed code here! + setState(() { + index = (index + 1) % customizations.length; + }); }, - backgroundColor: Colors.green, + foregroundColor: customizations[index].$1, + backgroundColor: customizations[index].$2, + shape: customizations[index].$3, child: const Icon(Icons.navigation), ), ); diff --git a/examples/api/lib/material/floating_action_button/floating_action_button.1.dart b/examples/api/lib/material/floating_action_button/floating_action_button.1.dart index 7180a52d43..9650247a25 100644 --- a/examples/api/lib/material/floating_action_button/floating_action_button.1.dart +++ b/examples/api/lib/material/floating_action_button/floating_action_button.1.dart @@ -13,8 +13,9 @@ class FloatingActionButtonExampleApp extends StatelessWidget { @override Widget build(BuildContext context) { - return const MaterialApp( - home: FabExample(), + return MaterialApp( + theme: ThemeData(colorSchemeSeed: const Color(0xff6750a4), useMaterial3: true), + home: const FabExample(), ); } } @@ -28,16 +29,77 @@ class FabExample extends StatelessWidget { appBar: AppBar( title: const Text('FloatingActionButton Sample'), ), - body: const Center( - child: Text('Press the button with a label below!'), - ), - floatingActionButton: FloatingActionButton.extended( - onPressed: () { - // Add your onPressed code here! - }, - label: const Text('Approve'), - icon: const Icon(Icons.thumb_up), - backgroundColor: Colors.pink, + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Small'), + const SizedBox(width: 16), + // An example of the small floating action button. + // + // https://m3.material.io/components/floating-action-button/specs#669a1be8-7271-48cb-a74d-dd502d73bda4 + FloatingActionButton.small( + onPressed: () { + // Add your onPressed code here! + }, + child: const Icon(Icons.add), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Regular'), + const SizedBox(width: 16), + // An example of the regular floating action button. + // + // https://m3.material.io/components/floating-action-button/specs#71504201-7bd1-423d-8bb7-07e0291743e5 + FloatingActionButton( + onPressed: () { + // Add your onPressed code here! + }, + child: const Icon(Icons.add), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Large'), + const SizedBox(width: 16), + // An example of the large floating action button. + // + // https://m3.material.io/components/floating-action-button/specs#9d7d3d6a-bab7-47cb-be32-5596fbd660fe + FloatingActionButton.large( + onPressed: () { + // Add your onPressed code here! + }, + child: const Icon(Icons.add), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Extended'), + const SizedBox(width: 16), + // An example of the extended floating action button. + // + // https://m3.material.io/components/extended-fab/specs#686cb8af-87c9-48e8-a3e1-db9da6f6c69b + FloatingActionButton.extended( + onPressed: () { + // Add your onPressed code here! + }, + label: const Text('Add'), + icon: const Icon(Icons.add), + ), + ], + ), + ], + ), ), ); } diff --git a/examples/api/lib/material/floating_action_button/floating_action_button.2.dart b/examples/api/lib/material/floating_action_button/floating_action_button.2.dart deleted file mode 100644 index 4eedfc1a2b..0000000000 --- a/examples/api/lib/material/floating_action_button/floating_action_button.2.dart +++ /dev/null @@ -1,44 +0,0 @@ -// 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:flutter/material.dart'; - -/// Flutter code sample for [FloatingActionButton]. - -void main() => runApp(const FloatingActionButtonExampleApp()); - -class FloatingActionButtonExampleApp extends StatelessWidget { - const FloatingActionButtonExampleApp({super.key}); - - @override - Widget build(BuildContext context) { - return MaterialApp( - theme: ThemeData(colorSchemeSeed: const Color(0xff6750a4), useMaterial3: true), - home: const FabExample(), - ); - } -} - -class FabExample extends StatelessWidget { - const FabExample({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('FloatingActionButton Sample'), - ), - body: const Center(child: Text('Press the button below!')), - // An example of the floating action button. - // - // https://m3.material.io/components/floating-action-button/specs - floatingActionButton: FloatingActionButton( - onPressed: () { - // Add your onPressed code here! - }, - child: const Icon(Icons.add), - ), - ); - } -} diff --git a/examples/api/lib/material/floating_action_button/floating_action_button.3.dart b/examples/api/lib/material/floating_action_button/floating_action_button.3.dart deleted file mode 100644 index 9650247a25..0000000000 --- a/examples/api/lib/material/floating_action_button/floating_action_button.3.dart +++ /dev/null @@ -1,106 +0,0 @@ -// 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:flutter/material.dart'; - -/// Flutter code sample for [FloatingActionButton]. - -void main() => runApp(const FloatingActionButtonExampleApp()); - -class FloatingActionButtonExampleApp extends StatelessWidget { - const FloatingActionButtonExampleApp({super.key}); - - @override - Widget build(BuildContext context) { - return MaterialApp( - theme: ThemeData(colorSchemeSeed: const Color(0xff6750a4), useMaterial3: true), - home: const FabExample(), - ); - } -} - -class FabExample extends StatelessWidget { - const FabExample({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('FloatingActionButton Sample'), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Small'), - const SizedBox(width: 16), - // An example of the small floating action button. - // - // https://m3.material.io/components/floating-action-button/specs#669a1be8-7271-48cb-a74d-dd502d73bda4 - FloatingActionButton.small( - onPressed: () { - // Add your onPressed code here! - }, - child: const Icon(Icons.add), - ), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Regular'), - const SizedBox(width: 16), - // An example of the regular floating action button. - // - // https://m3.material.io/components/floating-action-button/specs#71504201-7bd1-423d-8bb7-07e0291743e5 - FloatingActionButton( - onPressed: () { - // Add your onPressed code here! - }, - child: const Icon(Icons.add), - ), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Large'), - const SizedBox(width: 16), - // An example of the large floating action button. - // - // https://m3.material.io/components/floating-action-button/specs#9d7d3d6a-bab7-47cb-be32-5596fbd660fe - FloatingActionButton.large( - onPressed: () { - // Add your onPressed code here! - }, - child: const Icon(Icons.add), - ), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Extended'), - const SizedBox(width: 16), - // An example of the extended floating action button. - // - // https://m3.material.io/components/extended-fab/specs#686cb8af-87c9-48e8-a3e1-db9da6f6c69b - FloatingActionButton.extended( - onPressed: () { - // Add your onPressed code here! - }, - label: const Text('Add'), - icon: const Icon(Icons.add), - ), - ], - ), - ], - ), - ), - ); - } -} diff --git a/examples/api/test/material/floating_action_button/floating_action_button.0_test.dart b/examples/api/test/material/floating_action_button/floating_action_button.0_test.dart index 9fa5b6c497..bb1f1cbb4b 100644 --- a/examples/api/test/material/floating_action_button/floating_action_button.0_test.dart +++ b/examples/api/test/material/floating_action_button/floating_action_button.0_test.dart @@ -16,11 +16,30 @@ void main() { expect(find.byType(FloatingActionButton), findsOneWidget); expect(find.byIcon(Icons.navigation), findsOneWidget); - final Finder materialButtonFinder = find.byType(RawMaterialButton); RawMaterialButton getRawMaterialButtonWidget() { - return tester.widget(materialButtonFinder); + return tester.widget(find.byType(RawMaterialButton)); } + + Color? getIconColor() { + final RichText iconRichText = tester.widget( + find.descendant(of: find.byIcon(Icons.navigation), matching: find.byType(RichText)), + ); + return iconRichText.text.style?.color; + } + + await tester.tap(find.byType(FloatingActionButton)); + await tester.pumpAndSettle(); // Wait for the animation to finish. expect(getRawMaterialButtonWidget().fillColor, Colors.green); + + await tester.tap(find.byType(FloatingActionButton)); + await tester.pumpAndSettle(); // Wait for the animation to finish. + expect(getRawMaterialButtonWidget().fillColor, Colors.green); + expect(getIconColor(), Colors.white); + + await tester.tap(find.byType(FloatingActionButton)); + await tester.pumpAndSettle(); // Wait for the animation to finish. + expect(getRawMaterialButtonWidget().fillColor, Colors.green); + expect(getIconColor(), Colors.white); expect(getRawMaterialButtonWidget().shape, const CircleBorder()); }); } diff --git a/examples/api/test/material/floating_action_button/floating_action_button.1_test.dart b/examples/api/test/material/floating_action_button/floating_action_button.1_test.dart index 049d63576b..eb6ba0e8e7 100644 --- a/examples/api/test/material/floating_action_button/floating_action_button.1_test.dart +++ b/examples/api/test/material/floating_action_button/floating_action_button.1_test.dart @@ -8,20 +8,42 @@ import 'package:flutter_api_samples/material/floating_action_button/floating_act import 'package:flutter_test/flutter_test.dart'; void main() { - testWidgets('FloatingActionButton.extended', (WidgetTester tester) async { + testWidgets('FloatingActionButton variants', (WidgetTester tester) async { + RawMaterialButton getRawMaterialButtonWidget(Finder finder) { + return tester.widget(finder); + } + await tester.pumpWidget( const example.FloatingActionButtonExampleApp(), ); - expect(find.byType(FloatingActionButton), findsOneWidget); - expect(find.text('Approve'), findsOneWidget); - expect(find.byIcon(Icons.thumb_up), findsOneWidget); + final ThemeData theme = ThemeData(colorSchemeSeed: const Color(0xff6750a4), useMaterial3: true); - final Finder materialButtonFinder = find.byType(RawMaterialButton); - RawMaterialButton getRawMaterialButtonWidget() { - return tester.widget(materialButtonFinder); - } - expect(getRawMaterialButtonWidget().fillColor, Colors.pink); - expect(getRawMaterialButtonWidget().shape, const StadiumBorder()); + expect(find.byType(FloatingActionButton), findsNWidgets(4)); + expect(find.byIcon(Icons.add), findsNWidgets(4)); + + final Finder smallFabMaterialButton = find.byType(RawMaterialButton).at(0); + final RenderBox smallFabRenderBox = tester.renderObject(smallFabMaterialButton); + expect(smallFabRenderBox.size, const Size(48.0, 48.0)); + expect(getRawMaterialButtonWidget(smallFabMaterialButton).fillColor, theme.colorScheme.primaryContainer); + expect(getRawMaterialButtonWidget(smallFabMaterialButton).shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0))); + + final Finder regularFABMaterialButton = find.byType(RawMaterialButton).at(1); + final RenderBox regularFABRenderBox = tester.renderObject(regularFABMaterialButton); + expect(regularFABRenderBox.size, const Size(56.0, 56.0)); + expect(getRawMaterialButtonWidget(regularFABMaterialButton).fillColor, theme.colorScheme.primaryContainer); + expect(getRawMaterialButtonWidget(regularFABMaterialButton).shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(16.0))); + + final Finder largeFABMaterialButton = find.byType(RawMaterialButton).at(2); + final RenderBox largeFABRenderBox = tester.renderObject(largeFABMaterialButton); + expect(largeFABRenderBox.size, const Size(96.0, 96.0)); + expect(getRawMaterialButtonWidget(largeFABMaterialButton).fillColor, theme.colorScheme.primaryContainer); + expect(getRawMaterialButtonWidget(largeFABMaterialButton).shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(28.0))); + + final Finder extendedFABMaterialButton = find.byType(RawMaterialButton).at(3); + final RenderBox extendedFABRenderBox = tester.renderObject(extendedFABMaterialButton); + expect(extendedFABRenderBox.size, const Size(111.0, 56.0)); + expect(getRawMaterialButtonWidget(extendedFABMaterialButton).fillColor, theme.colorScheme.primaryContainer); + expect(getRawMaterialButtonWidget(extendedFABMaterialButton).shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(16.0))); }); } diff --git a/examples/api/test/material/floating_action_button/floating_action_button.2_test.dart b/examples/api/test/material/floating_action_button/floating_action_button.2_test.dart deleted file mode 100644 index d7cd0edd95..0000000000 --- a/examples/api/test/material/floating_action_button/floating_action_button.2_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -// 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:flutter/material.dart'; -import 'package:flutter_api_samples/material/floating_action_button/floating_action_button.2.dart' - as example; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - testWidgets('FloatingActionButton - Material 3', (WidgetTester tester) async { - RawMaterialButton getRawMaterialButtonWidget(Finder finder) { - return tester.widget(finder); - } - await tester.pumpWidget( - const example.FloatingActionButtonExampleApp(), - ); - - expect(find.byType(FloatingActionButton), findsOneWidget); - expect(find.byIcon(Icons.add), findsOneWidget); - - final ThemeData theme = ThemeData(colorSchemeSeed: const Color(0xff6750a4), useMaterial3: true); - final Finder materialButtonFinder = find.byType(RawMaterialButton); - expect(getRawMaterialButtonWidget(materialButtonFinder).fillColor, theme.colorScheme.primaryContainer); - expect(getRawMaterialButtonWidget(materialButtonFinder).shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(16.0))); - }); -} diff --git a/examples/api/test/material/floating_action_button/floating_action_button.3_test.dart b/examples/api/test/material/floating_action_button/floating_action_button.3_test.dart deleted file mode 100644 index 173ef748d0..0000000000 --- a/examples/api/test/material/floating_action_button/floating_action_button.3_test.dart +++ /dev/null @@ -1,49 +0,0 @@ -// 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:flutter/material.dart'; -import 'package:flutter_api_samples/material/floating_action_button/floating_action_button.3.dart' - as example; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - testWidgets('FloatingActionButton variants', (WidgetTester tester) async { - RawMaterialButton getRawMaterialButtonWidget(Finder finder) { - return tester.widget(finder); - } - - await tester.pumpWidget( - const example.FloatingActionButtonExampleApp(), - ); - - final ThemeData theme = ThemeData(colorSchemeSeed: const Color(0xff6750a4), useMaterial3: true); - - expect(find.byType(FloatingActionButton), findsNWidgets(4)); - expect(find.byIcon(Icons.add), findsNWidgets(4)); - - final Finder smallFabMaterialButton = find.byType(RawMaterialButton).at(0); - final RenderBox smallFabRenderBox = tester.renderObject(smallFabMaterialButton); - expect(smallFabRenderBox.size, const Size(48.0, 48.0)); - expect(getRawMaterialButtonWidget(smallFabMaterialButton).fillColor, theme.colorScheme.primaryContainer); - expect(getRawMaterialButtonWidget(smallFabMaterialButton).shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(12.0))); - - final Finder regularFABMaterialButton = find.byType(RawMaterialButton).at(1); - final RenderBox regularFABRenderBox = tester.renderObject(regularFABMaterialButton); - expect(regularFABRenderBox.size, const Size(56.0, 56.0)); - expect(getRawMaterialButtonWidget(regularFABMaterialButton).fillColor, theme.colorScheme.primaryContainer); - expect(getRawMaterialButtonWidget(regularFABMaterialButton).shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(16.0))); - - final Finder largeFABMaterialButton = find.byType(RawMaterialButton).at(2); - final RenderBox largeFABRenderBox = tester.renderObject(largeFABMaterialButton); - expect(largeFABRenderBox.size, const Size(96.0, 96.0)); - expect(getRawMaterialButtonWidget(largeFABMaterialButton).fillColor, theme.colorScheme.primaryContainer); - expect(getRawMaterialButtonWidget(largeFABMaterialButton).shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(28.0))); - - final Finder extendedFABMaterialButton = find.byType(RawMaterialButton).at(3); - final RenderBox extendedFABRenderBox = tester.renderObject(extendedFABMaterialButton); - expect(extendedFABRenderBox.size, const Size(111.0, 56.0)); - expect(getRawMaterialButtonWidget(extendedFABMaterialButton).fillColor, theme.colorScheme.primaryContainer); - expect(getRawMaterialButtonWidget(extendedFABMaterialButton).shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(16.0))); - }); -} diff --git a/packages/flutter/lib/src/material/floating_action_button.dart b/packages/flutter/lib/src/material/floating_action_button.dart index 1a580b3366..dbc08569fd 100644 --- a/packages/flutter/lib/src/material/floating_action_button.dart +++ b/packages/flutter/lib/src/material/floating_action_button.dart @@ -52,39 +52,21 @@ enum _FloatingActionButtonType { /// action button. /// /// {@tool dartpad} -/// This example shows how to display a [FloatingActionButton] in a -/// [Scaffold], with a pink [backgroundColor] and a thumbs up [Icon]. -/// -/// ![](https://flutter.github.io/assets-for-api-docs/assets/material/floating_action_button.png) +/// This example shows a [FloatingActionButton] in its usual position within a +/// [Scaffold]. Pressing the button cycles it through a few variations in its +/// [foregroundColor], [backgroundColor], and [shape]. The button automatically +/// animates its segue from one set of visual parameters to another. /// /// ** See code in examples/api/lib/material/floating_action_button/floating_action_button.0.dart ** /// {@end-tool} /// /// {@tool dartpad} -/// This example shows how to make an extended [FloatingActionButton] in a -/// [Scaffold], with a pink [backgroundColor], a thumbs up [Icon] and a -/// [Text] label that reads "Approve". -/// -/// ![](https://flutter.github.io/assets-for-api-docs/assets/material/floating_action_button_label.png) +/// This sample shows all the variants of [FloatingActionButton] widget as +/// described in: https://m3.material.io/components/floating-action-button/overview. /// /// ** See code in examples/api/lib/material/floating_action_button/floating_action_button.1.dart ** /// {@end-tool} /// -/// Material Design 3 introduced new types of floating action buttons. -/// {@tool dartpad} -/// This sample shows the creation of [FloatingActionButton] widget in the typical location in a Scaffold, -/// as described in: https://m3.material.io/components/floating-action-button/overview -/// -/// ** See code in examples/api/lib/material/floating_action_button/floating_action_button.2.dart ** -/// {@end-tool} -/// -/// {@tool dartpad} -/// This sample shows the creation of all the variants of [FloatingActionButton] widget as -/// described in: https://m3.material.io/components/floating-action-button/overview -/// -/// ** See code in examples/api/lib/material/floating_action_button/floating_action_button.3.dart ** -/// {@end-tool} -/// /// See also: /// /// * [Scaffold], in which floating action buttons typically live.