// 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/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; void main() { test('SegmentedButtonThemeData copyWith, ==, hashCode basics', () { expect(const SegmentedButtonThemeData(), const SegmentedButtonThemeData().copyWith()); expect(const SegmentedButtonThemeData().hashCode, const SegmentedButtonThemeData().copyWith().hashCode); const SegmentedButtonThemeData custom = SegmentedButtonThemeData( style: ButtonStyle(backgroundColor: MaterialStatePropertyAll<Color>(Colors.green)), selectedIcon: Icon(Icons.error), ); final SegmentedButtonThemeData copy = const SegmentedButtonThemeData().copyWith( style: custom.style, selectedIcon: custom.selectedIcon, ); expect(copy, custom); }); test('SegmentedButtonThemeData lerp special cases', () { expect(SegmentedButtonThemeData.lerp(null, null, 0), const SegmentedButtonThemeData()); const SegmentedButtonThemeData theme = SegmentedButtonThemeData(); expect(identical(SegmentedButtonThemeData.lerp(theme, theme, 0.5), theme), true); }); testWidgetsWithLeakTracking('Default SegmentedButtonThemeData debugFillProperties', (WidgetTester tester) async { final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder(); const SegmentedButtonThemeData().debugFillProperties(builder); final List<String> description = builder.properties .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info)) .map((DiagnosticsNode node) => node.toString()) .toList(); expect(description, <String>[]); }); testWidgetsWithLeakTracking('With no other configuration, defaults are used', (WidgetTester tester) async { final ThemeData theme = ThemeData(useMaterial3: true); await tester.pumpWidget( MaterialApp( theme: theme, home: Scaffold( body: Center( child: SegmentedButton<int>( segments: const <ButtonSegment<int>>[ ButtonSegment<int>(value: 1, label: Text('1')), ButtonSegment<int>(value: 2, label: Text('2')), ButtonSegment<int>(value: 3, label: Text('3'), enabled: false), ], selected: const <int>{2}, onSelectionChanged: (Set<int> selected) { }, ), ), ), ), ); // Test first segment, should be enabled { final Finder text = find.text('1'); final Finder parent = find.ancestor(of: text, matching: find.byType(Material)).first; final Finder selectedIcon = find.descendant(of: parent, matching: find.byIcon(Icons.check)); final Material material = tester.widget<Material>(parent); expect(material.color, Colors.transparent); expect(material.shape, const RoundedRectangleBorder()); expect(material.textStyle!.color, theme.colorScheme.onSurface); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); expect(material.textStyle!.fontWeight, FontWeight.w500); expect(selectedIcon, findsNothing); } // Test second segment, should be enabled and selected { final Finder text = find.text('2'); final Finder parent = find.ancestor(of: text, matching: find.byType(Material)).first; final Finder selectedIcon = find.descendant(of: parent, matching: find.byIcon(Icons.check)); final Material material = tester.widget<Material>(parent); expect(material.color, theme.colorScheme.secondaryContainer); expect(material.shape, const RoundedRectangleBorder()); expect(material.textStyle!.color, theme.colorScheme.onSecondaryContainer); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); expect(material.textStyle!.fontWeight, FontWeight.w500); expect(selectedIcon, findsOneWidget); } // Test last segment, should be disabled { final Finder text = find.text('3'); final Finder parent = find.ancestor(of: text, matching: find.byType(Material)).first; final Finder selectedIcon = find.descendant(of: parent, matching: find.byIcon(Icons.check)); final Material material = tester.widget<Material>(parent); expect(material.color, Colors.transparent); expect(material.shape, const RoundedRectangleBorder()); expect(material.textStyle!.color, theme.colorScheme.onSurface.withOpacity(0.38)); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); expect(material.textStyle!.fontWeight, FontWeight.w500); expect(selectedIcon, findsNothing); } }); testWidgetsWithLeakTracking('ThemeData.segmentedButtonTheme overrides defaults', (WidgetTester tester) async { final ThemeData theme = ThemeData( useMaterial3: true, segmentedButtonTheme: SegmentedButtonThemeData( style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) { if (states.contains(MaterialState.disabled)) { return Colors.blue; } if (states.contains(MaterialState.selected)) { return Colors.purple; } return null; }), foregroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) { if (states.contains(MaterialState.disabled)) { return Colors.yellow; } if (states.contains(MaterialState.selected)) { return Colors.brown; } else { return Colors.cyan; } }), ), selectedIcon: const Icon(Icons.error), ), ); await tester.pumpWidget( MaterialApp( theme: theme, home: Scaffold( body: Center( child: SegmentedButton<int>( segments: const <ButtonSegment<int>>[ ButtonSegment<int>(value: 1, label: Text('1')), ButtonSegment<int>(value: 2, label: Text('2')), ButtonSegment<int>(value: 3, label: Text('3'), enabled: false), ], selected: const <int>{2}, onSelectionChanged: (Set<int> selected) { }, ), ), ), ), ); // Test first segment, should be enabled { final Finder text = find.text('1'); final Finder parent = find.ancestor(of: text, matching: find.byType(Material)).first; final Finder selectedIcon = find.descendant(of: parent, matching: find.byIcon(Icons.error)); final Material material = tester.widget<Material>(parent); expect(material.color, Colors.transparent); expect(material.shape, const RoundedRectangleBorder()); expect(material.textStyle!.color, Colors.cyan); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); expect(material.textStyle!.fontWeight, FontWeight.w500); expect(selectedIcon, findsNothing); } // Test second segment, should be enabled and selected { final Finder text = find.text('2'); final Finder parent = find.ancestor(of: text, matching: find.byType(Material)).first; final Finder selectedIcon = find.descendant(of: parent, matching: find.byIcon(Icons.error)); final Material material = tester.widget<Material>(parent); expect(material.color, Colors.purple); expect(material.shape, const RoundedRectangleBorder()); expect(material.textStyle!.color, Colors.brown); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); expect(material.textStyle!.fontWeight, FontWeight.w500); expect(selectedIcon, findsOneWidget); } // Test last segment, should be disabled { final Finder text = find.text('3'); final Finder parent = find.ancestor(of: text, matching: find.byType(Material)).first; final Finder selectedIcon = find.descendant(of: parent, matching: find.byIcon(Icons.error)); final Material material = tester.widget<Material>(parent); expect(material.color, Colors.blue); expect(material.shape, const RoundedRectangleBorder()); expect(material.textStyle!.color, Colors.yellow); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); expect(material.textStyle!.fontWeight, FontWeight.w500); expect(selectedIcon, findsNothing); } }); testWidgetsWithLeakTracking('SegmentedButtonTheme overrides ThemeData and defaults', (WidgetTester tester) async { final SegmentedButtonThemeData global = SegmentedButtonThemeData( style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) { if (states.contains(MaterialState.disabled)) { return Colors.blue; } if (states.contains(MaterialState.selected)) { return Colors.purple; } return null; }), foregroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) { if (states.contains(MaterialState.disabled)) { return Colors.yellow; } if (states.contains(MaterialState.selected)) { return Colors.brown; } else { return Colors.cyan; } }), ), selectedIcon: const Icon(Icons.error), ); final SegmentedButtonThemeData segmentedTheme = SegmentedButtonThemeData( style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) { if (states.contains(MaterialState.disabled)) { return Colors.lightBlue; } if (states.contains(MaterialState.selected)) { return Colors.lightGreen; } return null; }), foregroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) { if (states.contains(MaterialState.disabled)) { return Colors.lime; } if (states.contains(MaterialState.selected)) { return Colors.amber; } else { return Colors.deepPurple; } }), ), selectedIcon: const Icon(Icons.plus_one), ); final ThemeData theme = ThemeData( useMaterial3: true, segmentedButtonTheme: global, ); await tester.pumpWidget( MaterialApp( theme: theme, home: SegmentedButtonTheme( data: segmentedTheme, child: Scaffold( body: Center( child: SegmentedButton<int>( segments: const <ButtonSegment<int>>[ ButtonSegment<int>(value: 1, label: Text('1')), ButtonSegment<int>(value: 2, label: Text('2')), ButtonSegment<int>(value: 3, label: Text('3'), enabled: false), ], selected: const <int>{2}, onSelectionChanged: (Set<int> selected) { }, ), ), ), ), ), ); // Test first segment, should be enabled { final Finder text = find.text('1'); final Finder parent = find.ancestor(of: text, matching: find.byType(Material)).first; final Finder selectedIcon = find.descendant(of: parent, matching: find.byIcon(Icons.plus_one)); final Material material = tester.widget<Material>(parent); expect(material.animationDuration, const Duration(milliseconds: 200)); expect(material.borderRadius, null); expect(material.color, Colors.transparent); expect(material.shape, const RoundedRectangleBorder()); expect(material.textStyle!.color, Colors.deepPurple); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); expect(material.textStyle!.fontWeight, FontWeight.w500); expect(selectedIcon, findsNothing); } // Test second segment, should be enabled and selected { final Finder text = find.text('2'); final Finder parent = find.ancestor(of: text, matching: find.byType(Material)).first; final Finder selectedIcon = find.descendant(of: parent, matching: find.byIcon(Icons.plus_one)); final Material material = tester.widget<Material>(parent); expect(material.animationDuration, const Duration(milliseconds: 200)); expect(material.borderRadius, null); expect(material.color, Colors.lightGreen); expect(material.shape, const RoundedRectangleBorder()); expect(material.textStyle!.color, Colors.amber); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); expect(material.textStyle!.fontWeight, FontWeight.w500); expect(selectedIcon, findsOneWidget); } // Test last segment, should be disabled { final Finder text = find.text('3'); final Finder parent = find.ancestor(of: text, matching: find.byType(Material)).first; final Finder selectedIcon = find.descendant(of: parent, matching: find.byIcon(Icons.plus_one)); final Material material = tester.widget<Material>(parent); expect(material.animationDuration, const Duration(milliseconds: 200)); expect(material.borderRadius, null); expect(material.color, Colors.lightBlue); expect(material.shape, const RoundedRectangleBorder()); expect(material.textStyle!.color, Colors.lime); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); expect(material.textStyle!.fontWeight, FontWeight.w500); expect(selectedIcon, findsNothing); } }); testWidgetsWithLeakTracking('Widget parameters overrides SegmentedTheme, ThemeData and defaults', (WidgetTester tester) async { final SegmentedButtonThemeData global = SegmentedButtonThemeData( style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) { if (states.contains(MaterialState.disabled)) { return Colors.blue; } if (states.contains(MaterialState.selected)) { return Colors.purple; } return null; }), foregroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) { if (states.contains(MaterialState.disabled)) { return Colors.yellow; } if (states.contains(MaterialState.selected)) { return Colors.brown; } else { return Colors.cyan; } }), ), selectedIcon: const Icon(Icons.error), ); final SegmentedButtonThemeData segmentedTheme = SegmentedButtonThemeData( style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) { if (states.contains(MaterialState.disabled)) { return Colors.lightBlue; } if (states.contains(MaterialState.selected)) { return Colors.lightGreen; } return null; }), foregroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) { if (states.contains(MaterialState.disabled)) { return Colors.lime; } if (states.contains(MaterialState.selected)) { return Colors.amber; } else { return Colors.deepPurple; } }), ), selectedIcon: const Icon(Icons.plus_one), ); final ThemeData theme = ThemeData( useMaterial3: true, segmentedButtonTheme: global, ); await tester.pumpWidget( MaterialApp( theme: theme, home: SegmentedButtonTheme( data: segmentedTheme, child: Scaffold( body: Center( child: SegmentedButton<int>( segments: const <ButtonSegment<int>>[ ButtonSegment<int>(value: 1, label: Text('1')), ButtonSegment<int>(value: 2, label: Text('2')), ButtonSegment<int>(value: 3, label: Text('3'), enabled: false), ], selected: const <int>{2}, onSelectionChanged: (Set<int> selected) { }, style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) { if (states.contains(MaterialState.disabled)) { return Colors.black12; } if (states.contains(MaterialState.selected)) { return Colors.grey; } return null; }), foregroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) { if (states.contains(MaterialState.disabled)) { return Colors.amberAccent; } if (states.contains(MaterialState.selected)) { return Colors.deepOrange; } else { return Colors.deepPurpleAccent; } }), ), selectedIcon: const Icon(Icons.alarm), ), ), ), ), ), ); // Test first segment, should be enabled { final Finder text = find.text('1'); final Finder parent = find.ancestor(of: text, matching: find.byType(Material)).first; final Finder selectedIcon = find.descendant(of: parent, matching: find.byIcon(Icons.alarm)); final Material material = tester.widget<Material>(parent); expect(material.animationDuration, const Duration(milliseconds: 200)); expect(material.borderRadius, null); expect(material.color, Colors.transparent); expect(material.shape, const RoundedRectangleBorder()); expect(material.textStyle!.color, Colors.deepPurpleAccent); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); expect(material.textStyle!.fontWeight, FontWeight.w500); expect(selectedIcon, findsNothing); } // Test second segment, should be enabled and selected { final Finder text = find.text('2'); final Finder parent = find.ancestor(of: text, matching: find.byType(Material)).first; final Finder selectedIcon = find.descendant(of: parent, matching: find.byIcon(Icons.alarm)); final Material material = tester.widget<Material>(parent); expect(material.animationDuration, const Duration(milliseconds: 200)); expect(material.borderRadius, null); expect(material.color, Colors.grey); expect(material.shape, const RoundedRectangleBorder()); expect(material.textStyle!.color, Colors.deepOrange); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); expect(material.textStyle!.fontWeight, FontWeight.w500); expect(selectedIcon, findsOneWidget); } // Test last segment, should be disabled { final Finder text = find.text('3'); final Finder parent = find.ancestor(of: text, matching: find.byType(Material)).first; final Finder selectedIcon = find.descendant(of: parent, matching: find.byIcon(Icons.alarm)); final Material material = tester.widget<Material>(parent); expect(material.animationDuration, const Duration(milliseconds: 200)); expect(material.borderRadius, null); expect(material.color, Colors.black12); expect(material.shape, const RoundedRectangleBorder()); expect(material.textStyle!.color, Colors.amberAccent); expect(material.textStyle!.fontFamily, 'Roboto'); expect(material.textStyle!.fontSize, 14); expect(material.textStyle!.fontWeight, FontWeight.w500); expect(selectedIcon, findsNothing); } }); }