mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
357 lines
14 KiB
Dart
357 lines
14 KiB
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:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import '../rendering/mock_canvas.dart';
|
|
|
|
void main() {
|
|
test('RadioThemeData copyWith, ==, hashCode basics', () {
|
|
expect(const RadioThemeData(), const RadioThemeData().copyWith());
|
|
expect(const RadioThemeData().hashCode, const RadioThemeData().copyWith().hashCode);
|
|
});
|
|
|
|
test('RadioThemeData defaults', () {
|
|
const RadioThemeData themeData = RadioThemeData();
|
|
expect(themeData.mouseCursor, null);
|
|
expect(themeData.fillColor, null);
|
|
expect(themeData.overlayColor, null);
|
|
expect(themeData.splashRadius, null);
|
|
expect(themeData.materialTapTargetSize, null);
|
|
expect(themeData.visualDensity, null);
|
|
|
|
const RadioTheme theme = RadioTheme(data: RadioThemeData(), child: SizedBox());
|
|
expect(theme.data.mouseCursor, null);
|
|
expect(theme.data.fillColor, null);
|
|
expect(theme.data.overlayColor, null);
|
|
expect(theme.data.splashRadius, null);
|
|
expect(theme.data.materialTapTargetSize, null);
|
|
expect(theme.data.visualDensity, null);
|
|
});
|
|
|
|
testWidgets('Default RadioThemeData debugFillProperties', (WidgetTester tester) async {
|
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
|
const RadioThemeData().debugFillProperties(builder);
|
|
|
|
final List<String> description = builder.properties
|
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
|
.map((DiagnosticsNode node) => node.toString())
|
|
.toList();
|
|
|
|
expect(description, <String>[]);
|
|
});
|
|
|
|
testWidgets('RadioThemeData implements debugFillProperties', (WidgetTester tester) async {
|
|
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
|
|
RadioThemeData(
|
|
mouseCursor: MaterialStateProperty.all(SystemMouseCursors.click),
|
|
fillColor: MaterialStateProperty.all(const Color(0xfffffff0)),
|
|
overlayColor: MaterialStateProperty.all(const Color(0xfffffff1)),
|
|
splashRadius: 1.0,
|
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
visualDensity: VisualDensity.standard,
|
|
).debugFillProperties(builder);
|
|
|
|
final List<String> description = builder.properties
|
|
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
|
|
.map((DiagnosticsNode node) => node.toString())
|
|
.toList();
|
|
|
|
expect(description[0], 'mouseCursor: MaterialStateProperty.all(SystemMouseCursor(click))');
|
|
expect(description[1], 'fillColor: MaterialStateProperty.all(Color(0xfffffff0))');
|
|
expect(description[2], 'overlayColor: MaterialStateProperty.all(Color(0xfffffff1))');
|
|
expect(description[3], 'splashRadius: 1.0');
|
|
expect(description[4], 'materialTapTargetSize: MaterialTapTargetSize.shrinkWrap');
|
|
expect(description[5], 'visualDensity: VisualDensity#00000(h: 0.0, v: 0.0)');
|
|
});
|
|
|
|
testWidgets('Radio is themeable', (WidgetTester tester) async {
|
|
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
|
|
|
const MouseCursor mouseCursor = SystemMouseCursors.text;
|
|
const Color defaultFillColor = Color(0xfffffff0);
|
|
const Color selectedFillColor = Color(0xfffffff1);
|
|
const Color focusOverlayColor = Color(0xfffffff2);
|
|
const Color hoverOverlayColor = Color(0xfffffff3);
|
|
const double splashRadius = 1.0;
|
|
const MaterialTapTargetSize materialTapTargetSize = MaterialTapTargetSize.shrinkWrap;
|
|
const VisualDensity visualDensity = VisualDensity(horizontal: 1, vertical: 1);
|
|
|
|
Widget buildRadio({bool selected = false, bool autofocus = false}) {
|
|
return MaterialApp(
|
|
theme: ThemeData(
|
|
radioTheme: RadioThemeData(
|
|
mouseCursor: MaterialStateProperty.all(mouseCursor),
|
|
fillColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
return selectedFillColor;
|
|
}
|
|
return defaultFillColor;
|
|
}),
|
|
overlayColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.focused)) {
|
|
return focusOverlayColor;
|
|
}
|
|
if (states.contains(MaterialState.hovered)) {
|
|
return hoverOverlayColor;
|
|
}
|
|
return null;
|
|
}),
|
|
splashRadius: splashRadius,
|
|
materialTapTargetSize: materialTapTargetSize,
|
|
visualDensity: visualDensity,
|
|
),
|
|
),
|
|
home: Scaffold(
|
|
body: Radio<int>(
|
|
onChanged: (int? int) {},
|
|
value: selected ? 1 : 0,
|
|
groupValue: 1,
|
|
autofocus: autofocus,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Radio.
|
|
await tester.pumpWidget(buildRadio());
|
|
await tester.pumpAndSettle();
|
|
expect(_getRadioMaterial(tester), paints..circle(color: defaultFillColor));
|
|
// Size from MaterialTapTargetSize.shrinkWrap with added VisualDensity.
|
|
expect(tester.getSize(_findRadio()), const Size(40.0, 40.0) + visualDensity.baseSizeAdjustment);
|
|
|
|
// Selected radio.
|
|
await tester.pumpWidget(buildRadio(selected: true));
|
|
await tester.pumpAndSettle();
|
|
expect(_getRadioMaterial(tester), paints..circle(color: selectedFillColor));
|
|
|
|
// Radio with hover.
|
|
await tester.pumpWidget(buildRadio());
|
|
await _pointGestureToRadio(tester);
|
|
await tester.pumpAndSettle();
|
|
expect(_getRadioMaterial(tester), paints..circle(color: hoverOverlayColor));
|
|
expect(RendererBinding.instance!.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
|
|
|
|
// Radio with focus.
|
|
await tester.pumpWidget(buildRadio(autofocus: true));
|
|
await tester.pumpAndSettle();
|
|
expect(_getRadioMaterial(tester), paints..circle(color: focusOverlayColor, radius: splashRadius));
|
|
});
|
|
|
|
testWidgets('Radio properties are taken over the theme values', (WidgetTester tester) async {
|
|
tester.binding.focusManager.highlightStrategy = FocusHighlightStrategy.alwaysTraditional;
|
|
|
|
const MouseCursor themeMouseCursor = SystemMouseCursors.click;
|
|
const Color themeDefaultFillColor = Color(0xfffffff0);
|
|
const Color themeSelectedFillColor = Color(0xfffffff1);
|
|
const Color themeFocusOverlayColor = Color(0xfffffff2);
|
|
const Color themeHoverOverlayColor = Color(0xfffffff3);
|
|
const double themeSplashRadius = 1.0;
|
|
const MaterialTapTargetSize themeMaterialTapTargetSize = MaterialTapTargetSize.padded;
|
|
const VisualDensity themeVisualDensity = VisualDensity.standard;
|
|
|
|
const MouseCursor mouseCursor = SystemMouseCursors.text;
|
|
const Color defaultFillColor = Color(0xfffffff0);
|
|
const Color selectedFillColor = Color(0xfffffff1);
|
|
const Color focusColor = Color(0xfffffff2);
|
|
const Color hoverColor = Color(0xfffffff3);
|
|
const double splashRadius = 2.0;
|
|
const MaterialTapTargetSize materialTapTargetSize = MaterialTapTargetSize.shrinkWrap;
|
|
const VisualDensity visualDensity = VisualDensity(horizontal: 1, vertical: 1);
|
|
|
|
Widget buildRadio({bool selected = false, bool autofocus = false}) {
|
|
return MaterialApp(
|
|
theme: ThemeData(
|
|
radioTheme: RadioThemeData(
|
|
mouseCursor: MaterialStateProperty.all(themeMouseCursor),
|
|
fillColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
return themeSelectedFillColor;
|
|
}
|
|
return themeDefaultFillColor;
|
|
}),
|
|
overlayColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.focused)) {
|
|
return themeFocusOverlayColor;
|
|
}
|
|
if (states.contains(MaterialState.hovered)) {
|
|
return themeHoverOverlayColor;
|
|
}
|
|
return null;
|
|
}),
|
|
splashRadius: themeSplashRadius,
|
|
materialTapTargetSize: themeMaterialTapTargetSize,
|
|
visualDensity: themeVisualDensity,
|
|
),
|
|
),
|
|
home: Scaffold(
|
|
body: Radio<int>(
|
|
onChanged: (int? int) {},
|
|
value: selected ? 0 : 1,
|
|
groupValue: 0,
|
|
autofocus: autofocus,
|
|
mouseCursor: mouseCursor,
|
|
fillColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
return selectedFillColor;
|
|
}
|
|
return defaultFillColor;
|
|
}),
|
|
focusColor: focusColor,
|
|
hoverColor: hoverColor,
|
|
splashRadius: splashRadius,
|
|
materialTapTargetSize: materialTapTargetSize,
|
|
visualDensity: visualDensity,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Radio.
|
|
await tester.pumpWidget(buildRadio());
|
|
await tester.pumpAndSettle();
|
|
expect(_getRadioMaterial(tester), paints..circle(color: defaultFillColor));
|
|
// Size from MaterialTapTargetSize.shrinkWrap with added VisualDensity.
|
|
expect(tester.getSize(_findRadio()), const Size(40.0, 40.0) + visualDensity.baseSizeAdjustment);
|
|
|
|
// Selected radio.
|
|
await tester.pumpWidget(buildRadio(selected: true));
|
|
await tester.pumpAndSettle();
|
|
expect(_getRadioMaterial(tester), paints..circle(color: selectedFillColor));
|
|
|
|
// Radio with hover.
|
|
await tester.pumpWidget(buildRadio());
|
|
await _pointGestureToRadio(tester);
|
|
await tester.pumpAndSettle();
|
|
expect(_getRadioMaterial(tester), paints..circle(color: hoverColor));
|
|
expect(RendererBinding.instance!.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
|
|
|
|
// Radio with focus.
|
|
await tester.pumpWidget(buildRadio(autofocus: true));
|
|
await tester.pumpAndSettle();
|
|
expect(_getRadioMaterial(tester), paints..circle(color: focusColor, radius: splashRadius));
|
|
});
|
|
|
|
testWidgets('Radio activeColor property is taken over the theme', (WidgetTester tester) async {
|
|
const Color themeDefaultFillColor = Color(0xfffffff0);
|
|
const Color themeSelectedFillColor = Color(0xfffffff1);
|
|
|
|
const Color selectedFillColor = Color(0xfffffff1);
|
|
|
|
Widget buildRadio({bool selected = false, bool autofocus = false}) {
|
|
return MaterialApp(
|
|
theme: ThemeData(
|
|
radioTheme: RadioThemeData(
|
|
fillColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
return themeSelectedFillColor;
|
|
}
|
|
return themeDefaultFillColor;
|
|
}),
|
|
),
|
|
),
|
|
home: Scaffold(
|
|
body: Radio<int>(
|
|
onChanged: (int? int) {},
|
|
value: selected ? 0 : 1,
|
|
groupValue: 0,
|
|
autofocus: autofocus,
|
|
activeColor: selectedFillColor,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Radio.
|
|
await tester.pumpWidget(buildRadio());
|
|
await tester.pumpAndSettle();
|
|
expect(_getRadioMaterial(tester), paints..circle(color: themeDefaultFillColor));
|
|
|
|
// Selected radio.
|
|
await tester.pumpWidget(buildRadio(selected: true));
|
|
await tester.pumpAndSettle();
|
|
expect(_getRadioMaterial(tester), paints..circle(color: selectedFillColor));
|
|
});
|
|
|
|
testWidgets('Radio theme overlay color resolves in active/pressed states', (WidgetTester tester) async {
|
|
const Color activePressedOverlayColor = Color(0xFF000001);
|
|
const Color inactivePressedOverlayColor = Color(0xFF000002);
|
|
|
|
Color? getOverlayColor(Set<MaterialState> states) {
|
|
if (states.contains(MaterialState.pressed)) {
|
|
if (states.contains(MaterialState.selected)) {
|
|
return activePressedOverlayColor;
|
|
}
|
|
return inactivePressedOverlayColor;
|
|
}
|
|
return null;
|
|
}
|
|
const double splashRadius = 24.0;
|
|
|
|
Widget buildRadio({required bool active}) {
|
|
return MaterialApp(
|
|
theme: ThemeData(
|
|
radioTheme: RadioThemeData(
|
|
overlayColor: MaterialStateProperty.resolveWith(getOverlayColor),
|
|
splashRadius: splashRadius,
|
|
),
|
|
),
|
|
home: Scaffold(
|
|
body: Radio<int>(
|
|
value: active ? 1 : 0,
|
|
groupValue: 1,
|
|
onChanged: (_) { },
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(buildRadio(active: false));
|
|
await tester.press(_findRadio());
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
_getRadioMaterial(tester),
|
|
paints
|
|
..circle(
|
|
color: inactivePressedOverlayColor,
|
|
radius: splashRadius,
|
|
),
|
|
reason: 'Inactive pressed Radio should have overlay color: $inactivePressedOverlayColor',
|
|
);
|
|
|
|
await tester.pumpWidget(buildRadio(active: true));
|
|
await tester.press(_findRadio());
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(
|
|
_getRadioMaterial(tester),
|
|
paints
|
|
..circle(
|
|
color: activePressedOverlayColor,
|
|
radius: splashRadius,
|
|
),
|
|
reason: 'Active pressed Radio should have overlay color: $activePressedOverlayColor',
|
|
);
|
|
});
|
|
}
|
|
|
|
Finder _findRadio() {
|
|
return find.byWidgetPredicate((Widget widget) => widget is Radio<int>);
|
|
}
|
|
|
|
Future<void> _pointGestureToRadio(WidgetTester tester) async {
|
|
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
|
await gesture.addPointer();
|
|
addTearDown(gesture.removePointer);
|
|
await gesture.moveTo(tester.getCenter(_findRadio()));
|
|
}
|
|
|
|
MaterialInkController? _getRadioMaterial(WidgetTester tester) {
|
|
return Material.of(tester.element(_findRadio()));
|
|
}
|