From 17c664fbbde0349c9789dfe77e2f7e6f41d5610f Mon Sep 17 00:00:00 2001 From: Taha Tesser Date: Mon, 16 Oct 2023 11:48:13 +0300 Subject: [PATCH] Fix `NavigationRail`'s indicator inkwell doesn't support transparent color. (#136359) fixes [NavigationRail can't have a transparent Hover color because there is always opacity set](https://github.com/flutter/flutter/issues/135866) ### Code sample
expand to view the code sample ```dart import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.green).copyWith( primary: Colors.transparent, ), ), home: const Example(), ); } } class Example extends StatefulWidget { const Example({Key? key}) : super(key: key); @override State createState() => _ExampleState(); } class _ExampleState extends State { int _selectedIndex = 0; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('NavigationRail Example'), ), body: Row( children: [ NavigationRail( selectedIndex: _selectedIndex, onDestinationSelected: (int index) { setState(() { _selectedIndex = index; }); }, labelType: NavigationRailLabelType.all, destinations: const [ NavigationRailDestination( icon: Icon(Icons.favorite_border), selectedIcon: Icon(Icons.favorite), label: Text('First'), ), NavigationRailDestination( icon: Icon(Icons.bookmark_border), selectedIcon: Icon(Icons.book), label: Text('Second'), ), NavigationRailDestination( icon: Icon(Icons.star_border), selectedIcon: Icon(Icons.star), label: Text('Third'), ), ], ), const VerticalDivider(thickness: 1, width: 1), Expanded( child: Center( child: Text('Selected Index: $_selectedIndex'), ), ) ], ), ); } } ```
--- .../lib/src/material/navigation_rail.dart | 11 +++- .../test/material/navigation_rail_test.dart | 57 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/material/navigation_rail.dart b/packages/flutter/lib/src/material/navigation_rail.dart index 0e47262b2e..cb86709e8c 100644 --- a/packages/flutter/lib/src/material/navigation_rail.dart +++ b/packages/flutter/lib/src/material/navigation_rail.dart @@ -782,6 +782,13 @@ class _RailDestination extends StatelessWidget { } final ColorScheme colors = Theme.of(context).colorScheme; + final bool primaryColorAlphaModified = colors.primary.alpha < 255.0; + final Color effectiveSplashColor = primaryColorAlphaModified + ? colors.primary + : colors.primary.withOpacity(0.12); + final Color effectiveHoverColor = primaryColorAlphaModified + ? colors.primary + : colors.primary.withOpacity(0.04); return Semantics( container: true, selected: selected, @@ -793,8 +800,8 @@ class _RailDestination extends StatelessWidget { onTap: disabled ? null : onTap, borderRadius: BorderRadius.all(Radius.circular(minWidth / 2.0)), customBorder: indicatorShape, - splashColor: colors.primary.withOpacity(0.12), - hoverColor: colors.primary.withOpacity(0.04), + splashColor: effectiveSplashColor, + hoverColor: effectiveHoverColor, useMaterial3: material3, indicatorOffset: indicatorOffset, applyXOffset: applyXOffset, diff --git a/packages/flutter/test/material/navigation_rail_test.dart b/packages/flutter/test/material/navigation_rail_test.dart index 3689777c1c..12ace20d39 100644 --- a/packages/flutter/test/material/navigation_rail_test.dart +++ b/packages/flutter/test/material/navigation_rail_test.dart @@ -3548,6 +3548,63 @@ void main() { expect(bcdLabelOpacity, closeTo(0.38, 0.01)); }); + testWidgetsWithLeakTracking('NavigationRail indicator inkwell can be transparent', (WidgetTester tester) async { + // This is a regression test for https://github.com/flutter/flutter/issues/135866. + final ThemeData theme = ThemeData( + colorScheme: const ColorScheme.light().copyWith(primary: Colors.transparent), + // Material 3 defaults to InkSparkle which is not testable using paints. + splashFactory: InkSplash.splashFactory, + ); + await tester.pumpWidget( + MaterialApp( + theme: theme, + home: Builder( + builder: (BuildContext context) { + return Scaffold( + body: Row( + children: [ + NavigationRail( + selectedIndex: 1, + destinations: const [ + NavigationRailDestination( + icon: Icon(Icons.favorite_border), + selectedIcon: Icon(Icons.favorite), + label: Text('ABC'), + ), + NavigationRailDestination( + icon: Icon(Icons.bookmark_border), + selectedIcon: Icon(Icons.bookmark), + label: Text('DEF'), + ), + ], + labelType: NavigationRailLabelType.all, + ), + const Expanded( + child: Text('body'), + ), + ], + ), + ); + }, + ), + )); + + final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(); + await gesture.moveTo(tester.getCenter(find.byIcon(Icons.favorite_border))); + await tester.pumpAndSettle(); + RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures'); + + expect(inkFeatures, paints..rect(color: Colors.transparent)); + + await gesture.down(tester.getCenter(find.byIcon(Icons.favorite_border))); + await tester.pump(); // Start the splash and highlight animations. + await tester.pump(const Duration(milliseconds: 800)); // Wait for splash and highlight to be well under way. + + inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures'); + expect(inkFeatures, paints..circle(color: Colors.transparent)); + }, skip: kIsWeb && !isCanvasKit); // https://github.com/flutter/flutter/issues/99933 + group('Material 2', () { // These tests are only relevant for Material 2. Once Material 2 // support is deprecated and the APIs are removed, these tests