Unverified Commit 0e57147d authored by hangyu's avatar hangyu Committed by GitHub

nav drawer (#115668)

parent 01c1e8e5
......@@ -29,6 +29,7 @@ import 'package:gen_defaults/checkbox_template.dart';
import 'package:gen_defaults/color_scheme_template.dart';
import 'package:gen_defaults/dialog_template.dart';
import 'package:gen_defaults/divider_template.dart';
import 'package:gen_defaults/drawer_template.dart';
import 'package:gen_defaults/fab_template.dart';
import 'package:gen_defaults/filter_chip_template.dart';
import 'package:gen_defaults/icon_button_template.dart';
......@@ -36,6 +37,7 @@ import 'package:gen_defaults/input_chip_template.dart';
import 'package:gen_defaults/input_decorator_template.dart';
import 'package:gen_defaults/menu_template.dart';
import 'package:gen_defaults/navigation_bar_template.dart';
import 'package:gen_defaults/navigation_drawer_template.dart';
import 'package:gen_defaults/navigation_rail_template.dart';
import 'package:gen_defaults/popup_menu_template.dart';
import 'package:gen_defaults/progress_indicator_template.dart';
......@@ -144,6 +146,7 @@ Future<void> main(List<String> args) async {
DialogFullscreenTemplate('DialogFullscreen', '$materialLib/dialog.dart', tokens).updateFile();
DialogTemplate('Dialog', '$materialLib/dialog.dart', tokens).updateFile();
DividerTemplate('Divider', '$materialLib/divider.dart', tokens).updateFile();
DrawerTemplate('Drawer', '$materialLib/drawer.dart', tokens).updateFile();
FABTemplate('FAB', '$materialLib/floating_action_button.dart', tokens).updateFile();
FilterChipTemplate('ChoiceChip', '$materialLib/choice_chip.dart', tokens).updateFile();
FilterChipTemplate('FilterChip', '$materialLib/filter_chip.dart', tokens).updateFile();
......@@ -152,6 +155,7 @@ Future<void> main(List<String> args) async {
InputDecoratorTemplate('InputDecorator', '$materialLib/input_decorator.dart', tokens).updateFile();
MenuTemplate('Menu', '$materialLib/menu_anchor.dart', tokens).updateFile();
NavigationBarTemplate('NavigationBar', '$materialLib/navigation_bar.dart', tokens).updateFile();
NavigationDrawerTemplate('NavigationDrawer', '$materialLib/navigation_drawer.dart', tokens).updateFile();
NavigationRailTemplate('NavigationRail', '$materialLib/navigation_rail.dart', tokens).updateFile();
PopupMenuTemplate('PopupMenu', '$materialLib/popup_menu.dart', tokens).updateFile();
ProgressIndicatorTemplate('ProgressIndicator', '$materialLib/progress_indicator.dart', tokens).updateFile();
......
// 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 'template.dart';
class DrawerTemplate extends TokenTemplate {
const DrawerTemplate(super.blockName, super.fileName, super.tokens);
@override
String generate() => '''
class _${blockName}DefaultsM3 extends DrawerThemeData {
const _${blockName}DefaultsM3(this.context)
: super(elevation: ${elevation("md.comp.navigation-drawer.modal.container")});
final BuildContext context;
@override
Color? get backgroundColor => ${componentColor("md.comp.navigation-drawer.container")};
@override
Color? get surfaceTintColor => ${colorOrTransparent("md.comp.navigation-drawer.container.surface-tint-layer.color")};
@override
Color? get shadowColor => ${colorOrTransparent("md.comp.navigation-drawer.container.shadow-color")};
// This don't appear to be tokens for this value, but it is
// shown in the spec.
@override
ShapeBorder? get shape => const RoundedRectangleBorder(
borderRadius: BorderRadius.horizontal(right: Radius.circular(16.0)),
);
// This don't appear to be tokens for this value, but it is
// shown in the spec.
@override
ShapeBorder? get endShape => const RoundedRectangleBorder(
borderRadius: BorderRadius.horizontal(left: Radius.circular(16.0)),
);
}
''';
}
// 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 'template.dart';
class NavigationDrawerTemplate extends TokenTemplate {
const NavigationDrawerTemplate(super.blockName, super.fileName, super.tokens);
@override
String generate() => '''
class _${blockName}DefaultsM3 extends NavigationDrawerThemeData {
const _${blockName}DefaultsM3(this.context)
: super(
elevation: ${elevation("md.comp.navigation-drawer.modal.container")},
tileHeight: ${tokens["md.comp.navigation-drawer.active-indicator.height"]},
indicatorShape: ${shape("md.comp.navigation-drawer.active-indicator")},
indicatorSize: const Size(${tokens["md.comp.navigation-drawer.active-indicator.width"]}, ${tokens["md.comp.navigation-drawer.active-indicator.height"]}),
);
final BuildContext context;
@override
Color? get backgroundColor => ${componentColor("md.comp.navigation-drawer.container")};
@override
Color? get surfaceTintColor => ${colorOrTransparent("md.comp.navigation-drawer.container.surface-tint-layer.color")};
@override
Color? get shadowColor => ${colorOrTransparent("md.comp.navigation-drawer.container.shadow-color")};
@override
Color? get indicatorColor => ${componentColor("md.comp.navigation-drawer.active-indicator")};
@override
MaterialStateProperty<IconThemeData?>? get iconTheme {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
return IconThemeData(
size: ${tokens["md.comp.navigation-drawer.icon.size"]},
color: states.contains(MaterialState.selected)
? ${componentColor("md.comp.navigation-drawer.active.icon.")}
: ${componentColor("md.comp.navigation-drawer.inactive.icon")},
);
});
}
@override
MaterialStateProperty<TextStyle?>? get labelTextStyle {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
final TextStyle style = ${textStyle("md.comp.navigation-drawer.label-text")}!;
return style.apply(
color: states.contains(MaterialState.selected)
? ${componentColor("md.comp.navigation-drawer.active.label-text")}
: ${componentColor("md.comp.navigation-drawer.inactive.label-text")},
);
});
}
}
''';
}
// 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.
// Flutter code sample for [NavigationDrawer] .
// Builds an adaptive navigation widget layout. When the screen width is less than
// 450, A [NavigationBar] will be displayed. Otherwise, a [NavigationRail] will be
// displayed on the left side, and also a button to open the [NavigationDrawer].
// All of these navigation widgets are built from an indentical list of data.
import 'package:flutter/material.dart';
class ExampleDestination {
const ExampleDestination(this.label, this.icon, this.selectedIcon);
final String label;
final Widget icon;
final Widget selectedIcon;
}
const List<ExampleDestination> destinations = <ExampleDestination>[
ExampleDestination('page 0', Icon(Icons.widgets_outlined), Icon(Icons.widgets)),
ExampleDestination('page 1', Icon(Icons.format_paint_outlined), Icon(Icons.format_paint)),
ExampleDestination('page 2', Icon(Icons.text_snippet_outlined), Icon(Icons.text_snippet)),
ExampleDestination('page 3', Icon(Icons.invert_colors_on_outlined), Icon(Icons.opacity)),
];
void main() {
runApp(
MaterialApp(
title: 'NavigationDrawer Example',
debugShowCheckedModeBanner: false,
theme: ThemeData(useMaterial3: true),
home: const NavigationDrawerExample(),
),
);
}
class NavigationDrawerExample extends StatefulWidget {
const NavigationDrawerExample({super.key});
@override
State<NavigationDrawerExample> createState() => _NavigationDrawerExampleState();
}
class _NavigationDrawerExampleState extends State<NavigationDrawerExample> {
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
int screenIndex = 0;
late bool showNavigationDrawer;
void handleScreenChanged(int selectedScreen) {
setState(() {
screenIndex = selectedScreen;
});
}
void openDrawer() {
scaffoldKey.currentState!.openEndDrawer();
}
Widget buildBottomBarScaffold(){
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text('Page Index = $screenIndex'),
],
),
),
bottomNavigationBar: NavigationBar(
selectedIndex: screenIndex,
onDestinationSelected: (int index) {
setState(() {
screenIndex = index;
});
},
destinations: destinations
.map((ExampleDestination destination) {
return NavigationDestination(
label: destination.label,
icon: destination.icon,
selectedIcon: destination.selectedIcon,
tooltip: destination.label,
);
})
.toList(),
),
);
}
Widget buildDrawerScaffold(BuildContext context){
return Scaffold(
key: scaffoldKey,
body: SafeArea(
bottom: false,
top: false,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: NavigationRail(
minWidth: 50,
destinations: destinations
.map((ExampleDestination destination) {
return NavigationRailDestination(
label: Text(destination.label),
icon: destination.icon,
selectedIcon: destination.selectedIcon,
);
})
.toList(),
selectedIndex: screenIndex,
useIndicator: true,
onDestinationSelected: (int index) {
setState(() {
screenIndex = index;
});
},
),
),
const VerticalDivider(thickness: 1, width: 1),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text('Page Index = $screenIndex'),
ElevatedButton(
onPressed: openDrawer,
child: const Text('Open Drawer'),
),
],
),
),
],
),
),
endDrawer: NavigationDrawer(
onDestinationSelected: handleScreenChanged,
selectedIndex: screenIndex,
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(28, 16, 16, 10),
child: Text(
'Header',
style: Theme.of(context).textTheme.titleSmall,
),
),
...destinations
.map((ExampleDestination destination) {
return NavigationDrawerDestination(
label: Text(destination.label),
icon: destination.icon,
selectedIcon: destination.selectedIcon,
);
}),
const Padding(
padding: EdgeInsets.fromLTRB(28, 16, 28, 10),
child: Divider(),
),
],
),
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
showNavigationDrawer = MediaQuery.of(context).size.width >= 450;
}
@override
Widget build(BuildContext context) {
return showNavigationDrawer ? buildDrawerScaffold(context) : buildBottomBarScaffold();
}
}
......@@ -124,6 +124,8 @@ export 'src/material/menu_theme.dart';
export 'src/material/mergeable_material.dart';
export 'src/material/navigation_bar.dart';
export 'src/material/navigation_bar_theme.dart';
export 'src/material/navigation_drawer.dart';
export 'src/material/navigation_drawer_theme.dart';
export 'src/material/navigation_rail.dart';
export 'src/material/navigation_rail_theme.dart';
export 'src/material/no_splash.dart';
......
......@@ -253,6 +253,8 @@ class Drawer extends StatelessWidget {
label = semanticLabel ?? MaterialLocalizations.of(context).drawerLabel;
}
final bool useMaterial3 = Theme.of(context).useMaterial3;
final bool isDrawerStart = DrawerController.maybeOf(context)?.alignment != DrawerAlignment.end;
final DrawerThemeData defaults= useMaterial3 ? _DrawerDefaultsM3(context): _DrawerDefaultsM2(context);
return Semantics(
scopesRoute: true,
namesRoute: true,
......@@ -261,11 +263,13 @@ class Drawer extends StatelessWidget {
child: ConstrainedBox(
constraints: BoxConstraints.expand(width: width ?? drawerTheme.width ?? _kWidth),
child: Material(
color: backgroundColor ?? drawerTheme.backgroundColor,
elevation: elevation ?? drawerTheme.elevation ?? 16.0,
shadowColor: shadowColor ?? drawerTheme.shadowColor ?? (useMaterial3 ? Colors.transparent : Theme.of(context).shadowColor),
surfaceTintColor: surfaceTintColor ?? drawerTheme.surfaceTintColor ?? (useMaterial3 ? Theme.of(context).colorScheme.surfaceTint : null),
shape: shape ?? drawerTheme.shape,
color: backgroundColor ?? drawerTheme.backgroundColor ?? defaults.backgroundColor,
elevation: elevation ?? drawerTheme.elevation ?? defaults.elevation!,
shadowColor: shadowColor ?? drawerTheme.shadowColor ?? defaults.shadowColor,
surfaceTintColor: surfaceTintColor ?? drawerTheme.surfaceTintColor ?? defaults.surfaceTintColor,
shape: shape ?? (isDrawerStart
? (drawerTheme.shape ?? defaults.shape)
: (drawerTheme.endShape ?? defaults.endShape)),
child: child,
),
),
......@@ -277,6 +281,20 @@ class Drawer extends StatelessWidget {
/// opened or closed.
typedef DrawerCallback = void Function(bool isOpened);
class _DrawerControllerScope extends InheritedWidget {
const _DrawerControllerScope({
required this.controller,
required super.child,
});
final DrawerController controller;
@override
bool updateShouldNotify(_DrawerControllerScope old) {
return controller != old.controller;
}
}
/// Provides interactive behavior for [Drawer] widgets.
///
/// Rarely used directly. Drawer controllers are typically created automatically
......@@ -379,6 +397,62 @@ class DrawerController extends StatefulWidget {
/// application was killed.
final bool isDrawerOpen;
/// The closest instance of [DrawerController] that encloses the given
/// context, or null if none is found.
///
/// {@tool snippet} Typical usage is as follows:
///
/// ```dart
/// DrawerController? controller = DrawerController.maybeOf(context);
/// ```
/// {@end-tool}
///
/// Calling this method will create a dependency on the closest
/// [DrawerController] in the [context], if there is one.
///
/// See also:
///
/// * [DrawerController.of], which is similar to this method, but asserts
/// if no [DrawerController] ancestor is found.
static DrawerController? maybeOf(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<_DrawerControllerScope>()?.controller;
}
/// The closest instance of [DrawerController] that encloses the given
/// context.
///
/// If no instance is found, this method will assert in debug mode and throw
/// an exception in release mode.
///
/// Calling this method will create a dependency on the closest
/// [DrawerController] in the [context].
///
/// {@tool snippet} Typical usage is as follows:
///
/// ```dart
/// DrawerController controller = DrawerController.of(context);
/// ```
/// {@end-tool}
static DrawerController of(BuildContext context) {
final DrawerController? controller = maybeOf(context);
assert(() {
if (controller == null) {
throw FlutterError(
'DrawerController.of() was called with a context that does not '
'contain a DrawerController widget.\n'
'No DrawerController widget ancestor could be found starting from '
'the context that was passed to DrawerController.of(). This can '
'happen because you are using a widget that looks for a DrawerController '
'ancestor, but no such ancestor exists.\n'
'The context used was:\n'
' $context',
);
}
return true;
}());
return controller!;
}
@override
DrawerControllerState createState() => DrawerControllerState();
}
......@@ -669,39 +743,42 @@ class DrawerControllerState extends State<DrawerController> with SingleTickerPro
}
assert(platformHasBackButton != null);
final Widget child = RepaintBoundary(
child: Stack(
children: <Widget>[
BlockSemantics(
child: ExcludeSemantics(
// On Android, the back button is used to dismiss a modal.
excluding: platformHasBackButton,
child: GestureDetector(
onTap: close,
child: Semantics(
label: MaterialLocalizations.of(context).modalBarrierDismissLabel,
child: Container( // The drawer's "scrim"
color: _scrimColorTween.evaluate(_controller),
final Widget child = _DrawerControllerScope(
controller: widget,
child: RepaintBoundary(
child: Stack(
children: <Widget>[
BlockSemantics(
child: ExcludeSemantics(
// On Android, the back button is used to dismiss a modal.
excluding: platformHasBackButton,
child: GestureDetector(
onTap: close,
child: Semantics(
label: MaterialLocalizations.of(context).modalBarrierDismissLabel,
child: Container( // The drawer's "scrim"
color: _scrimColorTween.evaluate(_controller),
),
),
),
),
),
),
Align(
alignment: _drawerOuterAlignment,
child: Align(
alignment: _drawerInnerAlignment,
widthFactor: _controller.value,
child: RepaintBoundary(
child: FocusScope(
key: _drawerKey,
node: _focusScopeNode,
child: widget.child,
Align(
alignment: _drawerOuterAlignment,
child: Align(
alignment: _drawerInnerAlignment,
widthFactor: _controller.value,
child: RepaintBoundary(
child: FocusScope(
key: _drawerKey,
node: _focusScopeNode,
child: widget.child,
),
),
),
),
),
],
],
),
),
);
......@@ -731,3 +808,55 @@ class DrawerControllerState extends State<DrawerController> with SingleTickerPro
);
}
}
class _DrawerDefaultsM2 extends DrawerThemeData {
const _DrawerDefaultsM2(this.context)
: super(elevation: 16.0);
final BuildContext context;
@override
Color? get shadowColor => Theme.of(context).shadowColor;
}
// BEGIN GENERATED TOKEN PROPERTIES - Drawer
// Do not edit by hand. The code between the "BEGIN GENERATED" and
// "END GENERATED" comments are generated from data in the Material
// Design token database by the script:
// dev/tools/gen_defaults/bin/gen_defaults.dart.
// Token database version: v0_141
class _DrawerDefaultsM3 extends DrawerThemeData {
const _DrawerDefaultsM3(this.context)
: super(elevation: 1.0);
final BuildContext context;
@override
Color? get backgroundColor => Theme.of(context).colorScheme.surface;
@override
Color? get surfaceTintColor => Theme.of(context).colorScheme.surfaceTint;
@override
Color? get shadowColor => Colors.transparent;
// This don't appear to be tokens for this value, but it is
// shown in the spec.
@override
ShapeBorder? get shape => const RoundedRectangleBorder(
borderRadius: BorderRadius.horizontal(right: Radius.circular(16.0)),
);
// This don't appear to be tokens for this value, but it is
// shown in the spec.
@override
ShapeBorder? get endShape => const RoundedRectangleBorder(
borderRadius: BorderRadius.horizontal(left: Radius.circular(16.0)),
);
}
// END GENERATED TOKEN PROPERTIES - Drawer
......@@ -41,6 +41,7 @@ class DrawerThemeData with Diagnosticable {
this.shadowColor,
this.surfaceTintColor,
this.shape,
this.endShape,
this.width,
});
......@@ -62,6 +63,9 @@ class DrawerThemeData with Diagnosticable {
/// Overrides the default value of [Drawer.shape].
final ShapeBorder? shape;
/// Overrides the default value of [Drawer.shape] for a end drawer.
final ShapeBorder? endShape;
/// Overrides the default value of [Drawer.width].
final double? width;
......@@ -74,6 +78,7 @@ class DrawerThemeData with Diagnosticable {
Color? shadowColor,
Color? surfaceTintColor,
ShapeBorder? shape,
ShapeBorder? endShape,
double? width,
}) {
return DrawerThemeData(
......@@ -83,6 +88,7 @@ class DrawerThemeData with Diagnosticable {
shadowColor: shadowColor ?? this.shadowColor,
surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor,
shape: shape ?? this.shape,
endShape: endShape ?? this.endShape,
width: width ?? this.width,
);
}
......@@ -104,6 +110,7 @@ class DrawerThemeData with Diagnosticable {
shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t),
surfaceTintColor: Color.lerp(a?.surfaceTintColor, b?.surfaceTintColor, t),
shape: ShapeBorder.lerp(a?.shape, b?.shape, t),
endShape: ShapeBorder.lerp(a?.endShape, b?.endShape, t),
width: lerpDouble(a?.width, b?.width, t),
);
}
......@@ -116,6 +123,7 @@ class DrawerThemeData with Diagnosticable {
shadowColor,
surfaceTintColor,
shape,
endShape,
width,
);
......@@ -134,6 +142,7 @@ class DrawerThemeData with Diagnosticable {
&& other.shadowColor == shadowColor
&& other.surfaceTintColor == surfaceTintColor
&& other.shape == shape
&& other.endShape == endShape
&& other.width == width;
}
......@@ -146,6 +155,7 @@ class DrawerThemeData with Diagnosticable {
properties.add(ColorProperty('shadowColor', shadowColor, defaultValue: null));
properties.add(ColorProperty('surfaceTintColor', surfaceTintColor, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>('shape', shape, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>('endShape', endShape, defaultValue: null));
properties.add(DoubleProperty('width', width, defaultValue: null));
}
}
......
......@@ -393,7 +393,7 @@ class _NavigationDestinationBuilder extends StatelessWidget {
this.tooltip,
});
/// Builds the icon for an destination in a [NavigationBar].
/// Builds the icon for a destination in a [NavigationBar].
///
/// To animate between unselected and selected, build the icon based on
/// [_NavigationDestinationInfo.selectedAnimation]. When the animation is 0,
......@@ -405,7 +405,7 @@ class _NavigationDestinationBuilder extends StatelessWidget {
/// animation is decreasing or dismissed.
final WidgetBuilder buildIcon;
/// Builds the label for an destination in a [NavigationBar].
/// Builds the label for a destination in a [NavigationBar].
///
/// To animate between unselected and selected, build the icon based on
/// [_NavigationDestinationInfo.selectedAnimation]. When the animation is
......
This diff is collapsed.
// 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' show lerpDouble;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'material_state.dart';
import 'navigation_drawer.dart';
import 'theme.dart';
// Examples can assume:
// late BuildContext context;
/// Defines default property values for descendant [NavigationDrawer]
/// widgets.
///
/// Descendant widgets obtain the current [NavigationDrawerThemeData] object
/// using `NavigationDrawerTheme.of(context)`. Instances of
/// [NavigationDrawerThemeData] can be customized with
/// [NavigationDrawerThemeData.copyWith].
///
/// Typically a [NavigationDrawerThemeData] is specified as part of the
/// overall [Theme] with [ThemeData.navigationDrawerTheme]. Alternatively, a
/// [NavigationDrawerTheme] inherited widget can be used to theme [NavigationDrawer]s
/// in a subtree of widgets.
///
/// All [NavigationDrawerThemeData] properties are `null` by default.
/// When null, the [NavigationDrawer] will provide its own defaults based on the
/// overall [Theme]'s textTheme and colorScheme. See the individual
/// [NavigationDrawer] properties for details.
///
/// See also:
///
/// * [ThemeData], which describes the overall theme information for the
/// application.
@immutable
class NavigationDrawerThemeData with Diagnosticable {
/// Creates a theme that can be used for [ThemeData.navigationDrawerTheme] and
/// [NavigationDrawerTheme].
const NavigationDrawerThemeData({
this.tileHeight,
this.backgroundColor,
this.elevation,
this.shadowColor,
this.surfaceTintColor,
this.indicatorColor,
this.indicatorShape,
this.indicatorSize,
this.labelTextStyle,
this.iconTheme,
});
/// Overrides the default height of [NavigationDrawerDestination].
final double? tileHeight;
/// Overrides the default value of [NavigationDrawer.backgroundColor].
final Color? backgroundColor;
/// Overrides the default value of [NavigationDrawer.elevation].
final double? elevation;
/// Overrides the default value of [NavigationDrawer.shadowColor].
final Color? shadowColor;
/// Overrides the default value of [NavigationDrawer.surfaceTintColor].
final Color? surfaceTintColor;
/// Overrides the default value of [NavigationDrawer]'s selection indicator.
final Color? indicatorColor;
/// Overrides the default shape of the [NavigationDrawer]'s selection indicator.
final ShapeBorder? indicatorShape;
/// Overrides the default size of the [NavigationDrawer]'s selection indicator.
final Size? indicatorSize;
/// The style to merge with the default text style for
/// [NavigationDestination] labels.
///
/// You can use this to specify a different style when the label is selected.
final MaterialStateProperty<TextStyle?>? labelTextStyle;
/// The theme to merge with the default icon theme for
/// [NavigationDestination] icons.
///
/// You can use this to specify a different icon theme when the icon is
/// selected.
final MaterialStateProperty<IconThemeData?>? iconTheme;
/// Creates a copy of this object with the given fields replaced with the
/// new values.
NavigationDrawerThemeData copyWith({
double? tileHeight,
Color? backgroundColor,
double? elevation,
Color? shadowColor,
Color? surfaceTintColor,
Color? indicatorColor,
ShapeBorder? indicatorShape,
Size? indicatorSize,
MaterialStateProperty<TextStyle?>? labelTextStyle,
MaterialStateProperty<IconThemeData?>? iconTheme,
}) {
return NavigationDrawerThemeData(
tileHeight: tileHeight ?? this.tileHeight,
backgroundColor: backgroundColor ?? this.backgroundColor,
elevation: elevation ?? this.elevation,
shadowColor: shadowColor ?? this.shadowColor,
surfaceTintColor: surfaceTintColor ?? this.surfaceTintColor,
indicatorColor: indicatorColor ?? this.indicatorColor,
indicatorShape: indicatorShape ?? this.indicatorShape,
indicatorSize: indicatorSize ?? this.indicatorSize,
labelTextStyle: labelTextStyle ?? this.labelTextStyle,
iconTheme: iconTheme ?? this.iconTheme,
);
}
/// Linearly interpolate between two navigation rail themes.
///
/// If both arguments are null then null is returned.
///
/// {@macro dart.ui.shadow.lerp}
static NavigationDrawerThemeData? lerp(
NavigationDrawerThemeData? a, NavigationDrawerThemeData? b, double t) {
assert(t != null);
if (a == null && b == null) {
return null;
}
return NavigationDrawerThemeData(
tileHeight: lerpDouble(a?.tileHeight, b?.tileHeight, t),
backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
elevation: lerpDouble(a?.elevation, b?.elevation, t),
shadowColor: Color.lerp(a?.shadowColor, b?.shadowColor, t),
surfaceTintColor: Color.lerp(a?.surfaceTintColor, b?.surfaceTintColor, t),
indicatorColor: Color.lerp(a?.indicatorColor, b?.indicatorColor, t),
indicatorShape: ShapeBorder.lerp(a?.indicatorShape, b?.indicatorShape, t),
indicatorSize: Size.lerp(a?.indicatorSize, a?.indicatorSize, t),
labelTextStyle: MaterialStateProperty.lerp<TextStyle?>(
a?.labelTextStyle, b?.labelTextStyle, t, TextStyle.lerp),
iconTheme: MaterialStateProperty.lerp<IconThemeData?>(
a?.iconTheme, b?.iconTheme, t, IconThemeData.lerp),
);
}
@override
int get hashCode => Object.hash(
tileHeight,
backgroundColor,
elevation,
shadowColor,
surfaceTintColor,
indicatorColor,
indicatorShape,
indicatorSize,
labelTextStyle,
iconTheme,
);
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
if (other.runtimeType != runtimeType) {
return false;
}
return other is NavigationDrawerThemeData &&
other.tileHeight == tileHeight &&
other.backgroundColor == backgroundColor &&
other.elevation == elevation &&
other.shadowColor == shadowColor &&
other.surfaceTintColor == surfaceTintColor &&
other.indicatorColor == indicatorColor &&
other.indicatorShape == indicatorShape &&
other.indicatorSize == indicatorSize &&
other.labelTextStyle == labelTextStyle &&
other.iconTheme == iconTheme;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
.add(DoubleProperty('tileHeight', tileHeight, defaultValue: null));
properties.add(
ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
properties.add(DoubleProperty('elevation', elevation, defaultValue: null));
properties.add(ColorProperty('shadowColor', shadowColor, defaultValue: null));
properties.add(ColorProperty('surfaceTintColor', surfaceTintColor,
defaultValue: null));
properties.add(
ColorProperty('indicatorColor', indicatorColor, defaultValue: null));
properties.add(DiagnosticsProperty<ShapeBorder>(
'indicatorShape', indicatorShape,
defaultValue: null));
properties.add(DiagnosticsProperty<Size>('indicatorSize', indicatorSize,
defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<TextStyle?>>(
'labelTextStyle', labelTextStyle,
defaultValue: null));
properties.add(DiagnosticsProperty<MaterialStateProperty<IconThemeData?>>(
'iconTheme', iconTheme,
defaultValue: null));
}
}
/// An inherited widget that defines visual properties for [NavigationDrawer]s and
/// [NavigationDestination]s in this widget's subtree.
///
/// Values specified here are used for [NavigationDrawer] properties that are not
/// given an explicit non-null value.
///
/// See also:
///
/// * [ThemeData.navigationDrawerTheme], which describes the
/// [NavigationDrawerThemeData] in the overall theme for the application.
class NavigationDrawerTheme extends InheritedTheme {
/// Creates a navigation rail theme that controls the
/// [NavigationDrawerThemeData] properties for a [NavigationDrawer].
///
/// The data argument must not be null.
const NavigationDrawerTheme({
super.key,
required this.data,
required super.child,
}) : assert(data != null);
/// Specifies the background color, label text style, icon theme, and label
/// type values for descendant [NavigationDrawer] widgets.
final NavigationDrawerThemeData data;
/// The closest instance of this class that encloses the given context.
///
/// If there is no enclosing [NavigationDrawerTheme] widget, then
/// [ThemeData.navigationDrawerTheme] is used.
static NavigationDrawerThemeData of(BuildContext context) {
final NavigationDrawerTheme? navigationDrawerTheme =
context.dependOnInheritedWidgetOfExactType<NavigationDrawerTheme>();
return navigationDrawerTheme?.data ??
Theme.of(context).navigationDrawerTheme;
}
@override
Widget wrap(BuildContext context, Widget child) {
return NavigationDrawerTheme(data: data, child: child);
}
@override
bool updateShouldNotify(NavigationDrawerTheme oldWidget) =>
data != oldWidget.data;
}
......@@ -41,6 +41,7 @@ import 'menu_bar_theme.dart';
import 'menu_button_theme.dart';
import 'menu_theme.dart';
import 'navigation_bar_theme.dart';
import 'navigation_drawer_theme.dart';
import 'navigation_rail_theme.dart';
import 'outlined_button_theme.dart';
import 'page_transitions_theme.dart';
......@@ -357,6 +358,7 @@ class ThemeData with Diagnosticable {
MenuButtonThemeData? menuButtonTheme,
MenuThemeData? menuTheme,
NavigationBarThemeData? navigationBarTheme,
NavigationDrawerThemeData? navigationDrawerTheme,
NavigationRailThemeData? navigationRailTheme,
OutlinedButtonThemeData? outlinedButtonTheme,
PopupMenuThemeData? popupMenuTheme,
......@@ -610,6 +612,7 @@ class ThemeData with Diagnosticable {
menuButtonTheme ??= const MenuButtonThemeData();
menuTheme ??= const MenuThemeData();
navigationBarTheme ??= const NavigationBarThemeData();
navigationDrawerTheme ??= const NavigationDrawerThemeData();
navigationRailTheme ??= const NavigationRailThemeData();
outlinedButtonTheme ??= const OutlinedButtonThemeData();
popupMenuTheme ??= const PopupMenuThemeData();
......@@ -706,6 +709,7 @@ class ThemeData with Diagnosticable {
menuButtonTheme: menuButtonTheme,
menuTheme: menuTheme,
navigationBarTheme: navigationBarTheme,
navigationDrawerTheme: navigationDrawerTheme,
navigationRailTheme: navigationRailTheme,
outlinedButtonTheme: outlinedButtonTheme,
popupMenuTheme: popupMenuTheme,
......@@ -818,6 +822,7 @@ class ThemeData with Diagnosticable {
required this.menuButtonTheme,
required this.menuTheme,
required this.navigationBarTheme,
required this.navigationDrawerTheme,
required this.navigationRailTheme,
required this.outlinedButtonTheme,
required this.popupMenuTheme,
......@@ -988,6 +993,7 @@ class ThemeData with Diagnosticable {
assert(menuButtonTheme != null),
assert(menuTheme != null),
assert(navigationBarTheme != null),
assert(navigationDrawerTheme != null),
assert(navigationRailTheme != null),
assert(outlinedButtonTheme != null),
assert(popupMenuTheme != null),
......@@ -1586,6 +1592,10 @@ class ThemeData with Diagnosticable {
/// of a [NavigationBar].
final NavigationBarThemeData navigationBarTheme;
/// A theme for customizing the background color, text style, and icon themes
/// of a [NavigationDrawer].
final NavigationDrawerThemeData navigationDrawerTheme;
/// A theme for customizing the background color, elevation, text style, and
/// icon themes of a [NavigationRail].
final NavigationRailThemeData navigationRailTheme;
......@@ -1883,6 +1893,7 @@ class ThemeData with Diagnosticable {
MenuButtonThemeData? menuButtonTheme,
MenuThemeData? menuTheme,
NavigationBarThemeData? navigationBarTheme,
NavigationDrawerThemeData? navigationDrawerTheme,
NavigationRailThemeData? navigationRailTheme,
OutlinedButtonThemeData? outlinedButtonTheme,
PopupMenuThemeData? popupMenuTheme,
......@@ -2046,6 +2057,7 @@ class ThemeData with Diagnosticable {
menuButtonTheme: menuButtonTheme ?? this.menuButtonTheme,
menuTheme: menuTheme ?? this.menuTheme,
navigationBarTheme: navigationBarTheme ?? this.navigationBarTheme,
navigationDrawerTheme: navigationDrawerTheme ?? this.navigationDrawerTheme,
navigationRailTheme: navigationRailTheme ?? this.navigationRailTheme,
outlinedButtonTheme: outlinedButtonTheme ?? this.outlinedButtonTheme,
popupMenuTheme: popupMenuTheme ?? this.popupMenuTheme,
......@@ -2251,6 +2263,7 @@ class ThemeData with Diagnosticable {
menuButtonTheme: MenuButtonThemeData.lerp(a.menuButtonTheme, b.menuButtonTheme, t)!,
menuTheme: MenuThemeData.lerp(a.menuTheme, b.menuTheme, t)!,
navigationBarTheme: NavigationBarThemeData.lerp(a.navigationBarTheme, b.navigationBarTheme, t)!,
navigationDrawerTheme: NavigationDrawerThemeData.lerp(a.navigationDrawerTheme, b.navigationDrawerTheme, t)!,
navigationRailTheme: NavigationRailThemeData.lerp(a.navigationRailTheme, b.navigationRailTheme, t)!,
outlinedButtonTheme: OutlinedButtonThemeData.lerp(a.outlinedButtonTheme, b.outlinedButtonTheme, t)!,
popupMenuTheme: PopupMenuThemeData.lerp(a.popupMenuTheme, b.popupMenuTheme, t)!,
......@@ -2358,6 +2371,7 @@ class ThemeData with Diagnosticable {
other.menuButtonTheme == menuButtonTheme &&
other.menuTheme == menuTheme &&
other.navigationBarTheme == navigationBarTheme &&
other.navigationDrawerTheme == navigationDrawerTheme &&
other.navigationRailTheme == navigationRailTheme &&
other.outlinedButtonTheme == outlinedButtonTheme &&
other.popupMenuTheme == popupMenuTheme &&
......@@ -2462,6 +2476,7 @@ class ThemeData with Diagnosticable {
menuButtonTheme,
menuTheme,
navigationBarTheme,
navigationDrawerTheme,
navigationRailTheme,
outlinedButtonTheme,
popupMenuTheme,
......@@ -2568,6 +2583,7 @@ class ThemeData with Diagnosticable {
properties.add(DiagnosticsProperty<MenuButtonThemeData>('menuButtonTheme', menuButtonTheme, defaultValue: defaultData.menuButtonTheme, level: DiagnosticLevel.debug));
properties.add(DiagnosticsProperty<MenuThemeData>('menuTheme', menuTheme, defaultValue: defaultData.menuTheme, level: DiagnosticLevel.debug));
properties.add(DiagnosticsProperty<NavigationBarThemeData>('navigationBarTheme', navigationBarTheme, defaultValue: defaultData.navigationBarTheme, level: DiagnosticLevel.debug));
properties.add(DiagnosticsProperty<NavigationDrawerThemeData>('navigationDrawerTheme', navigationDrawerTheme, defaultValue: defaultData.navigationDrawerTheme, level: DiagnosticLevel.debug));
properties.add(DiagnosticsProperty<NavigationRailThemeData>('navigationRailTheme', navigationRailTheme, defaultValue: defaultData.navigationRailTheme, level: DiagnosticLevel.debug));
properties.add(DiagnosticsProperty<OutlinedButtonThemeData>('outlinedButtonTheme', outlinedButtonTheme, defaultValue: defaultData.outlinedButtonTheme, level: DiagnosticLevel.debug));
properties.add(DiagnosticsProperty<PopupMenuThemeData>('popupMenuTheme', popupMenuTheme, defaultValue: defaultData.popupMenuTheme, level: DiagnosticLevel.debug));
......
......@@ -70,7 +70,40 @@ void main() {
expect(_drawerMaterial(tester).elevation, 16.0);
expect(_drawerMaterial(tester).shadowColor, useMaterial3 ? Colors.transparent : ThemeData().shadowColor);
expect(_drawerMaterial(tester).surfaceTintColor, useMaterial3 ? ThemeData().colorScheme.surfaceTint : null);
expect(_drawerMaterial(tester).shape, null);
expect(
_drawerMaterial(tester).shape,
useMaterial3
? const RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(right: Radius.circular(16.0)))
: null,
);
expect(_scrim(tester).color, Colors.black54);
expect(_drawerRenderBox(tester).size.width, 304.0);
});
testWidgets('Default values are used when no Drawer or DrawerThemeData properties are specified in end drawer', (WidgetTester tester) async {
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
final bool useMaterial3 = ThemeData().useMaterial3;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
key: scaffoldKey,
endDrawer: const Drawer(),
),
),
);
scaffoldKey.currentState!.openEndDrawer();
await tester.pumpAndSettle();
expect(_drawerMaterial(tester).color, null);
expect(_drawerMaterial(tester).elevation, 16.0);
expect(_drawerMaterial(tester).shadowColor, useMaterial3 ? Colors.transparent : ThemeData().shadowColor);
expect(_drawerMaterial(tester).surfaceTintColor, useMaterial3 ? ThemeData().colorScheme.surfaceTint : null);
expect(
_drawerMaterial(tester).shape,
useMaterial3
? const RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(left: Radius.circular(16.0)))
: null,
);
expect(_scrim(tester).color, Colors.black54);
expect(_drawerRenderBox(tester).size.width, 304.0);
});
......
// 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';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Navigation drawer updates destinations when tapped',
(WidgetTester tester) async {
int mutatedIndex = -1;
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
final ThemeData theme= ThemeData.from(colorScheme: const ColorScheme.light());
widgetSetup(tester, 3000, windowHeight: 3000);
final Widget widget = _buildWidget(
scaffoldKey,
NavigationDrawer(
children: <Widget>[
Text('Headline', style: theme.textTheme.bodyLarge),
NavigationDrawerDestination(
icon: Icon(Icons.ac_unit, color: theme.iconTheme.color),
label: Text('AC', style: theme.textTheme.bodySmall),
),
NavigationDrawerDestination(
icon: Icon(Icons.access_alarm, color: theme.iconTheme.color),
label: Text('Alarm',style: theme.textTheme.bodySmall),
),
],
onDestinationSelected: (int i) {
mutatedIndex = i;
},
),
);
await tester.pumpWidget(widget);
scaffoldKey.currentState!.openDrawer();
await tester.pump();
expect(find.text('Headline'), findsOneWidget);
expect(find.text('AC'), findsOneWidget);
expect(find.text('Alarm'), findsOneWidget);
await tester.pump(const Duration(seconds: 1)); // animation done
await tester.tap(find.text('Alarm'));
expect(mutatedIndex, 1);
await tester.tap(find.text('AC'));
expect(mutatedIndex, 0);
});
testWidgets('NavigationDrawer can update background color',
(WidgetTester tester) async {
const Color color = Colors.yellow;
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
final ThemeData theme= ThemeData.from(colorScheme: const ColorScheme.light());
await tester.pumpWidget(
_buildWidget(
scaffoldKey,
NavigationDrawer(
backgroundColor: color,
children: <Widget>[
Text('Headline', style: theme.textTheme.bodyLarge),
NavigationDrawerDestination(
icon: Icon(Icons.ac_unit, color: theme.iconTheme.color),
label: Text('AC', style: theme.textTheme.bodySmall),
),
NavigationDrawerDestination(
icon: Icon(Icons.access_alarm, color: theme.iconTheme.color),
label: Text('Alarm',style: theme.textTheme.bodySmall),
),
],
onDestinationSelected: (int i) {},
),
),
);
scaffoldKey.currentState!.openDrawer();
await tester.pump(const Duration(seconds: 1)); // animation done
expect(_getMaterial(tester).color, equals(color));
});
testWidgets('NavigationDrawer can update elevation',
(WidgetTester tester) async {
const double elevation = 42.0;
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
final ThemeData theme= ThemeData.from(colorScheme: const ColorScheme.light());
final NavigationDrawer drawer = NavigationDrawer(
elevation: elevation,
children: <Widget>[
Text('Headline', style: theme.textTheme.bodyLarge),
NavigationDrawerDestination(
icon: Icon(Icons.ac_unit, color: theme.iconTheme.color),
label: Text('AC', style: theme.textTheme.bodySmall),
),
NavigationDrawerDestination(
icon: Icon(Icons.access_alarm, color: theme.iconTheme.color),
label: Text('Alarm',style: theme.textTheme.bodySmall),
),
],
);
await tester.pumpWidget(
_buildWidget(
scaffoldKey,
drawer,
),
);
scaffoldKey.currentState!.openDrawer();
await tester.pump(const Duration(seconds: 1));
expect(_getMaterial(tester).elevation, equals(elevation));
});
testWidgets(
'NavigationDrawer uses proper defaults when no parameters are given',
(WidgetTester tester) async {
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
final ThemeData theme= ThemeData.from(colorScheme: const ColorScheme.light());
// M3 settings from the token database.
await tester.pumpWidget(
_buildWidget(
scaffoldKey,
Theme(
data: ThemeData.light().copyWith(useMaterial3: true),
child: NavigationDrawer(
children: <Widget>[
Text('Headline', style: theme.textTheme.bodyLarge),
NavigationDrawerDestination(
icon: Icon(Icons.ac_unit, color: theme.iconTheme.color),
label: Text('AC', style: theme.textTheme.bodySmall),
),
NavigationDrawerDestination(
icon: Icon(Icons.access_alarm, color: theme.iconTheme.color),
label: Text('Alarm',style: theme.textTheme.bodySmall),
),
],
onDestinationSelected: (int i) {},
),
),
),
);
scaffoldKey.currentState!.openDrawer();
await tester.pump(const Duration(seconds: 1));
expect(_getMaterial(tester).color, ThemeData().colorScheme.surface);
expect(_getMaterial(tester).surfaceTintColor,
ThemeData().colorScheme.surfaceTint);
expect(_getMaterial(tester).elevation, 1);
expect(_indicator(tester)?.color, const Color(0xff2196f3));
expect(_indicator(tester)?.shape, const StadiumBorder());
});
testWidgets('Navigation drawer semantics', (WidgetTester tester) async {
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
final ThemeData theme= ThemeData.from(colorScheme: const ColorScheme.light());
Widget widget({int selectedIndex = 0}) {
return _buildWidget(
scaffoldKey,
NavigationDrawer(
selectedIndex: selectedIndex,
children: <Widget>[
Text('Headline', style: theme.textTheme.bodyLarge),
NavigationDrawerDestination(
icon: Icon(Icons.ac_unit, color: theme.iconTheme.color),
label: Text('AC', style: theme.textTheme.bodySmall),
),
NavigationDrawerDestination(
icon: Icon(Icons.access_alarm, color: theme.iconTheme.color),
label: Text('Alarm',style: theme.textTheme.bodySmall),
),
],
),
);
}
await tester.pumpWidget(widget());
scaffoldKey.currentState!.openDrawer();
await tester.pump(const Duration(seconds: 1));
expect(
tester.getSemantics(find.text('AC')),
matchesSemantics(
label: 'AC\nTab 1 of 2',
textDirection: TextDirection.ltr,
isFocusable: true,
isSelected: true,
hasTapAction: true,
),
);
expect(
tester.getSemantics(find.text('Alarm')),
matchesSemantics(
label: 'Alarm\nTab 2 of 2',
textDirection: TextDirection.ltr,
isFocusable: true,
hasTapAction: true,
),
);
await tester.pumpWidget(widget(selectedIndex: 1));
expect(
tester.getSemantics(find.text('AC')),
matchesSemantics(
label: 'AC\nTab 1 of 2',
textDirection: TextDirection.ltr,
isFocusable: true,
hasTapAction: true,
),
);
expect(
tester.getSemantics(find.text('Alarm')),
matchesSemantics(
label: 'Alarm\nTab 2 of 2',
textDirection: TextDirection.ltr,
isFocusable: true,
isSelected: true,
hasTapAction: true,
),
);
});
}
Widget _buildWidget(GlobalKey<ScaffoldState> scaffoldKey, Widget child) {
return MaterialApp(
theme: ThemeData.light(),
home: Scaffold(
key: scaffoldKey,
drawer: child,
body: Container(),
),
);
}
Material _getMaterial(WidgetTester tester) {
return tester.firstWidget<Material>(
find.descendant(
of: find.byType(NavigationDrawer), matching: find.byType(Material)),
);
}
ShapeDecoration? _indicator(WidgetTester tester) {
return tester
.firstWidget<Container>(
find.descendant(
of: find.byType(FadeTransition),
matching: find.byType(Container),
),
)
.decoration as ShapeDecoration?;
}
void widgetSetup(WidgetTester tester, double windowWidth,
{double? windowHeight}) {
final double height = windowHeight ?? 1000;
tester.binding.window.devicePixelRatioTestValue = 2;
final double dpi = tester.binding.window.devicePixelRatio;
tester.binding.window.physicalSizeTestValue =
Size(windowWidth * dpi, height * dpi);
}
......@@ -794,6 +794,7 @@ void main() {
menuButtonTheme: MenuButtonThemeData(style: MenuItemButton.styleFrom(backgroundColor: Colors.black)),
menuTheme: const MenuThemeData(style: MenuStyle(backgroundColor: MaterialStatePropertyAll<Color>(Colors.black))),
navigationBarTheme: const NavigationBarThemeData(backgroundColor: Colors.black),
navigationDrawerTheme: const NavigationDrawerThemeData(backgroundColor: Colors.black),
navigationRailTheme: const NavigationRailThemeData(backgroundColor: Colors.black),
outlinedButtonTheme: OutlinedButtonThemeData(style: OutlinedButton.styleFrom(foregroundColor: Colors.blue)),
popupMenuTheme: const PopupMenuThemeData(color: Colors.black),
......@@ -913,6 +914,7 @@ void main() {
menuButtonTheme: MenuButtonThemeData(style: MenuItemButton.styleFrom(backgroundColor: Colors.black)),
menuTheme: const MenuThemeData(style: MenuStyle(backgroundColor: MaterialStatePropertyAll<Color>(Colors.white))),
navigationBarTheme: const NavigationBarThemeData(backgroundColor: Colors.white),
navigationDrawerTheme: const NavigationDrawerThemeData(backgroundColor: Colors.white),
navigationRailTheme: const NavigationRailThemeData(backgroundColor: Colors.white),
outlinedButtonTheme: const OutlinedButtonThemeData(),
popupMenuTheme: const PopupMenuThemeData(color: Colors.white),
......@@ -1018,6 +1020,7 @@ void main() {
menuButtonTheme: otherTheme.menuButtonTheme,
menuTheme: otherTheme.menuTheme,
navigationBarTheme: otherTheme.navigationBarTheme,
navigationDrawerTheme: otherTheme.navigationDrawerTheme,
navigationRailTheme: otherTheme.navigationRailTheme,
outlinedButtonTheme: otherTheme.outlinedButtonTheme,
popupMenuTheme: otherTheme.popupMenuTheme,
......@@ -1260,6 +1263,7 @@ void main() {
'menuButtonTheme',
'menuTheme',
'navigationBarTheme',
'navigationDrawerTheme',
'navigationRailTheme',
'outlinedButtonTheme',
'popupMenuTheme',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment