diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart index 10365b510c..43d7375439 100644 --- a/packages/flutter/lib/src/material/tabs.dart +++ b/packages/flutter/lib/src/material/tabs.dart @@ -1604,6 +1604,7 @@ class TabPageSelector extends StatelessWidget { final ColorTween selectedColorTween = ColorTween(begin: fixColor, end: fixSelectedColor); final ColorTween previousColorTween = ColorTween(begin: fixSelectedColor, end: fixColor); final TabController? tabController = controller ?? DefaultTabController.of(context); + final MaterialLocalizations localizations = MaterialLocalizations.of(context); assert(() { if (tabController == null) { throw FlutterError( @@ -1624,7 +1625,7 @@ class TabPageSelector extends StatelessWidget { animation: animation, builder: (BuildContext context, Widget? child) { return Semantics( - label: 'Page ${tabController.index + 1} of ${tabController.length}', + label: localizations.tabLabel(tabIndex: tabController.index + 1, tabCount: tabController.length), child: Row( mainAxisSize: MainAxisSize.min, children: List.generate(tabController.length, (int tabIndex) { diff --git a/packages/flutter/test/material/page_selector_test.dart b/packages/flutter/test/material/page_selector_test.dart index d0063f195a..4a2f3c7722 100644 --- a/packages/flutter/test/material/page_selector_test.dart +++ b/packages/flutter/test/material/page_selector_test.dart @@ -9,34 +9,41 @@ const Color kSelectedColor = Color(0xFF00FF00); const Color kUnselectedColor = Colors.transparent; Widget buildFrame(TabController tabController, { Color? color, Color? selectedColor, double indicatorSize = 12.0 }) { - return Directionality( - textDirection: TextDirection.ltr, - child: Theme( - data: ThemeData(colorScheme: const ColorScheme.light().copyWith(secondary: kSelectedColor)), - child: SizedBox.expand( - child: Center( - child: SizedBox( - width: 400.0, - height: 400.0, - child: Column( - children: [ - TabPageSelector( - controller: tabController, - color: color, - selectedColor: selectedColor, - indicatorSize: indicatorSize, - ), - Flexible( - child: TabBarView( + return Localizations( + locale: const Locale('en', 'US'), + delegates: const >[ + DefaultMaterialLocalizations.delegate, + DefaultWidgetsLocalizations.delegate, + ], + child: Directionality( + textDirection: TextDirection.ltr, + child: Theme( + data: ThemeData(colorScheme: const ColorScheme.light().copyWith(secondary: kSelectedColor)), + child: SizedBox.expand( + child: Center( + child: SizedBox( + width: 400.0, + height: 400.0, + child: Column( + children: [ + TabPageSelector( controller: tabController, - children: const [ - Center(child: Text('0')), - Center(child: Text('1')), - Center(child: Text('2')), - ], + color: color, + selectedColor: selectedColor, + indicatorSize: indicatorSize, ), - ), - ], + Flexible( + child: TabBarView( + controller: tabController, + children: const [ + Center(child: Text('0')), + Center(child: Text('1')), + Center(child: Text('2')), + ], + ), + ), + ], + ), ), ), ), diff --git a/packages/flutter/test/material/tabs_test.dart b/packages/flutter/test/material/tabs_test.dart index 2c5f13ecc4..5bd2223856 100644 --- a/packages/flutter/test/material/tabs_test.dart +++ b/packages/flutter/test/material/tabs_test.dart @@ -3857,6 +3857,89 @@ void main() { final Tab firstTab = tester.widget(find.widgetWithIcon(Tab, Icons.check)); expect(firstTab.height, 85); }); + + testWidgets('Test semantics of TabPageSelector', (WidgetTester tester) async { + final SemanticsTester semantics = SemanticsTester(tester); + + final TabController controller = TabController( + vsync: const TestVSync(), + length: 2, + initialIndex: 0, + ); + + await tester.pumpWidget( + boilerplate( + child: Column( + children: [ + TabBar( + controller: controller, + indicatorWeight: 30.0, + tabs: const [Tab(text: 'TAB1'), Tab(text: 'TAB2')], + ), + Flexible( + child: TabBarView( + controller: controller, + children: const [Text('PAGE1'), Text('PAGE2')], + ), + ), + Expanded( + child: TabPageSelector( + controller: controller + ) + ), + ], + ), + ), + ); + + final TestSemantics expectedSemantics = TestSemantics.root( + children: [ + TestSemantics.rootChild( + label: 'Tab 1 of 2', + id: 1, + rect: TestSemantics.fullScreen, + children: [ + TestSemantics( + label: 'TAB1\nTab 1 of 2', + flags: [SemanticsFlag.isFocusable, SemanticsFlag.isSelected], + id: 2, + rect: TestSemantics.fullScreen, + actions: 1, + ), + TestSemantics( + label: 'TAB2\nTab 2 of 2', + flags: [SemanticsFlag.isFocusable], + id: 3, + rect: TestSemantics.fullScreen, + actions: [SemanticsAction.tap], + ), + TestSemantics( + id: 4, + rect: TestSemantics.fullScreen, + children: [ + TestSemantics( + id: 6, + rect: TestSemantics.fullScreen, + actions: [SemanticsAction.scrollLeft], + children: [ + TestSemantics( + id: 5, + rect: TestSemantics.fullScreen, + label: 'PAGE1' + ), + ] + ), + ], + ), + ], + ), + ], + ); + + expect(semantics, hasSemantics(expectedSemantics, ignoreRect: true, ignoreTransform: true)); + + semantics.dispose(); + }); } class KeepAliveInk extends StatefulWidget { diff --git a/packages/flutter_localizations/test/material/tabs_test.dart b/packages/flutter_localizations/test/material/tabs_test.dart new file mode 100644 index 0000000000..25c739cab6 --- /dev/null +++ b/packages/flutter_localizations/test/material/tabs_test.dart @@ -0,0 +1,58 @@ +// 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_localizations/flutter_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('Test semantics of TabPageSelector in pt-BR', + (WidgetTester tester) async { + final TabController controller = TabController( + vsync: const TestVSync(), + length: 2, + initialIndex: 0, + ); + + await tester.pumpWidget( + Localizations( + locale: const Locale('pt', 'BR'), + delegates: const >[ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + child: Directionality( + textDirection: TextDirection.ltr, + child: Material( + child: Column( + children: [ + TabBar( + controller: controller, + indicatorWeight: 30.0, + tabs: const [Tab(text: 'TAB1'), Tab(text: 'TAB2')], + ), + Flexible( + child: TabBarView( + controller: controller, + children: const [Text('PAGE1'), Text('PAGE2')], + ), + ), + Expanded(child: TabPageSelector(controller: controller)), + ], + ), + ), + ), + ), + ); + + final SemanticsHandle handle = tester.ensureSemantics(); + + expect(tester.getSemantics(find.byType(TabPageSelector)), + matchesSemantics(label: 'Guia 1 de 2')); + + handle.dispose(); + }); +}