From ce24dd6a76d2bb9d0a66425d5533e17bd323e7e6 Mon Sep 17 00:00:00 2001 From: Kostia Sokolovskyi Date: Wed, 25 Sep 2024 17:53:28 +0200 Subject: [PATCH] Add WidgetStateBorderSide example and tests for it. (#155559) Fixes https://github.com/flutter/flutter/issues/155557 ### Description - Adds example for `WidgetStateBorderSide` - Adds tests for `examples/api/lib/widgets/widget_state/widget_state_border_side.0.dart` --- .../widget_state_border_side.0.dart | 60 +++++++++++++++ .../widget_state_border_side.0_test.dart | 76 +++++++++++++++++++ .../flutter/lib/src/widgets/widget_state.dart | 7 ++ 3 files changed, 143 insertions(+) create mode 100644 examples/api/lib/widgets/widget_state/widget_state_border_side.0.dart create mode 100644 examples/api/test/widgets/widget_state/widget_state_border_side.0_test.dart diff --git a/examples/api/lib/widgets/widget_state/widget_state_border_side.0.dart b/examples/api/lib/widgets/widget_state/widget_state_border_side.0.dart new file mode 100644 index 0000000000..06762c0f0b --- /dev/null +++ b/examples/api/lib/widgets/widget_state/widget_state_border_side.0.dart @@ -0,0 +1,60 @@ +// 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 [WidgetStateBorderSide]. + +void main() { + runApp(const WidgetStateBorderSideExampleApp()); +} + +class WidgetStateBorderSideExampleApp extends StatelessWidget { + const WidgetStateBorderSideExampleApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar(title: const Text('WidgetStateBorderSide Sample')), + body: const Center( + child: WidgetStateBorderSideExample(), + ), + ), + ); + } +} + +class WidgetStateBorderSideExample extends StatefulWidget { + const WidgetStateBorderSideExample({super.key}); + + @override + State createState() => _WidgetStateBorderSideExampleState(); +} + +class _WidgetStateBorderSideExampleState extends State { + bool _isSelected = true; + + @override + Widget build(BuildContext context) { + return FilterChip( + label: const Text('Select chip'), + selected: _isSelected, + onSelected: (bool value) { + setState(() { + _isSelected = value; + }); + }, + side: const WidgetStateBorderSide.fromMap( + { + WidgetState.pressed: BorderSide(color: Colors.green), + WidgetState.hovered: BorderSide(color: Colors.blue), + WidgetState.selected: BorderSide(color: Colors.red), + // Resolves to null if no keys match, deferring to the default value + // of the theme or widget. + }, + ), + ); + } +} diff --git a/examples/api/test/widgets/widget_state/widget_state_border_side.0_test.dart b/examples/api/test/widgets/widget_state/widget_state_border_side.0_test.dart new file mode 100644 index 0000000000..548fe4fd8e --- /dev/null +++ b/examples/api/test/widgets/widget_state/widget_state_border_side.0_test.dart @@ -0,0 +1,76 @@ +// 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 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter_api_samples/widgets/widget_state/widget_state_border_side.0.dart' + as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + Finder findByBorderColor(Color color) { + return find.byWidgetPredicate((Widget widget) { + if (widget is! Material) { + return false; + } + + final ShapeBorder? shape = widget.shape; + return shape is OutlinedBorder && shape.side.color == color; + }); + } + + testWidgets('FilterChip displays the blue colored border when hovered', (WidgetTester tester) async { + await tester.pumpWidget( + const example.WidgetStateBorderSideExampleApp(), + ); + + // Hover over the FilterChip. + final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.moveTo(tester.getCenter(find.byType(FilterChip))); + + await tester.pumpAndSettle(); + + expect(findByBorderColor(Colors.blue), findsOneWidget); + }); + + testWidgets('FilterChip displays the green colored border when pressed', (WidgetTester tester) async { + await tester.pumpWidget( + const example.WidgetStateBorderSideExampleApp(), + ); + + // Press on the FilterChip. + final TestGesture gesture = await tester.createGesture(); + await gesture.down(tester.getCenter(find.byType(FilterChip))); + + await tester.pumpAndSettle(); + + expect(findByBorderColor(Colors.green), findsOneWidget); + }); + + testWidgets('FilterChip displays the red colored border when selected', (WidgetTester tester) async { + await tester.pumpWidget( + const example.WidgetStateBorderSideExampleApp(), + ); + + expect(findByBorderColor(Colors.red), findsOneWidget); + }); + + testWidgets('FilterChip displays the correct border color when not selected', (WidgetTester tester) async { + await tester.pumpWidget( + const example.WidgetStateBorderSideExampleApp(), + ); + + await tester.tap(find.byType(FilterChip)); + await tester.pumpAndSettle(); + + final ThemeData theme = Theme.of(tester.element(find.byType(FilterChip))); + + // FilterChip's border color defaults to ColorScheme.outlineVariant. + expect( + findByBorderColor(theme.colorScheme.outlineVariant), + findsOneWidget, + ); + }); +} diff --git a/packages/flutter/lib/src/widgets/widget_state.dart b/packages/flutter/lib/src/widgets/widget_state.dart index 14d46bbc96..0cd809def4 100644 --- a/packages/flutter/lib/src/widgets/widget_state.dart +++ b/packages/flutter/lib/src/widgets/widget_state.dart @@ -480,6 +480,13 @@ class _EnabledAndDisabledMouseCursor extends WidgetStateMouseCursor { /// property values. [WidgetStateBorderSide] should only be used with widgets that document /// their support, like [ActionChip.side]. /// +/// {@tool dartpad} +/// This example defines a [WidgetStateBorderSide] which resolves to different +/// border colors depending on how the user interacts with it. +/// +/// ** See code in examples/api/lib/widgets/widget_state/widget_state_border_side.0.dart ** +/// {@end-tool} +/// /// This class should only be used for parameters which are documented to take /// [WidgetStateBorderSide], otherwise only the default state will be used. ///