Unverified Commit 66a84d1f authored by Qun Cheng's avatar Qun Cheng Committed by GitHub

Migrate `IconButton` to Material 3 - Part 1 (#105176)

* Added standard IconButton for M3 with new ButtonStyle field

* Added IconButton examples for standard, filled, filled_tonal, and outlined types
Co-authored-by: 's avatarQun Cheng <quncheng@google.com>
parent 17185191
......@@ -22,6 +22,7 @@ import 'package:gen_defaults/button_template.dart';
import 'package:gen_defaults/card_template.dart';
import 'package:gen_defaults/dialog_template.dart';
import 'package:gen_defaults/fab_template.dart';
import 'package:gen_defaults/icon_button_template.dart';
import 'package:gen_defaults/navigation_bar_template.dart';
import 'package:gen_defaults/navigation_rail_template.dart';
import 'package:gen_defaults/surface_tint.dart';
......@@ -55,6 +56,9 @@ Future<void> main(List<String> args) async {
'fab_large_primary.json',
'fab_primary.json',
'fab_small_primary.json',
'icon_button.json',
'icon_button_filled.json',
'icon_button_filled_tonal.json',
'motion.json',
'navigation_bar.json',
'navigation_rail.json',
......@@ -86,6 +90,7 @@ Future<void> main(List<String> args) async {
CardTemplate('$materialLib/card.dart', tokens).updateFile();
DialogTemplate('$materialLib/dialog.dart', tokens).updateFile();
FABTemplate('$materialLib/floating_action_button.dart', tokens).updateFile();
IconButtonTemplate('$materialLib/icon_button.dart', tokens).updateFile();
NavigationBarTemplate('$materialLib/navigation_bar.dart', tokens).updateFile();
NavigationRailTemplate('$materialLib/navigation_rail.dart', tokens).updateFile();
SurfaceTintTemplate('$materialLib/elevation_overlay.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 IconButtonTemplate extends TokenTemplate {
const IconButtonTemplate(super.fileName, super.tokens)
: super(colorSchemePrefix: '_colors.',
);
@override
String generate() => '''
// Generated version ${tokens["version"]}
class _TokenDefaultsM3 extends ButtonStyle {
_TokenDefaultsM3(this.context)
: super(
animationDuration: kThemeChangeDuration,
enableFeedback: true,
alignment: Alignment.center,
);
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
// No default text style
@override
MaterialStateProperty<Color?>? get backgroundColor =>
ButtonStyleButton.allOrNull<Color>(Colors.transparent);
@override
MaterialStateProperty<Color?>? get foregroundColor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled))
return ${componentColor('md.comp.icon-button.disabled.icon')};
return ${componentColor('md.comp.icon-button.unselected.icon')};
});
@override
MaterialStateProperty<Color?>? get overlayColor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.hovered))
return ${componentColor('md.comp.icon-button.unselected.hover.state-layer')};
if (states.contains(MaterialState.focused))
return ${componentColor('md.comp.icon-button.unselected.focus.state-layer')};
if (states.contains(MaterialState.pressed))
return ${componentColor('md.comp.icon-button.unselected.pressed.state-layer')};
return null;
});
// No default shadow color
// No default surface tint color
@override
MaterialStateProperty<double>? get elevation =>
ButtonStyleButton.allOrNull<double>(0.0);
@override
MaterialStateProperty<EdgeInsetsGeometry>? get padding =>
ButtonStyleButton.allOrNull<EdgeInsetsGeometry>(const EdgeInsets.all(8.0));
@override
MaterialStateProperty<Size>? get minimumSize =>
ButtonStyleButton.allOrNull<Size>(const Size(${tokens["md.comp.icon-button.state-layer.size"]}, ${tokens["md.comp.icon-button.state-layer.size"]}));
// No default fixedSize
@override
MaterialStateProperty<Size>? get maximumSize =>
ButtonStyleButton.allOrNull<Size>(Size.infinite);
// No default side
@override
MaterialStateProperty<OutlinedBorder>? get shape =>
ButtonStyleButton.allOrNull<OutlinedBorder>(${shape("md.comp.icon-button.state-layer")});
@override
MaterialStateProperty<MouseCursor?>? get mouseCursor =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled))
return SystemMouseCursors.basic;
return SystemMouseCursors.click;
});
@override
VisualDensity? get visualDensity => Theme.of(context).visualDensity;
@override
MaterialTapTargetSize? get tapTargetSize => Theme.of(context).materialTapTargetSize;
@override
InteractiveInkFeatureFactory? get splashFactory => Theme.of(context).splashFactory;
}
''';
}
// 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 IconButton
import 'package:flutter/material.dart';
void main() {
runApp(const IconButtonApp());
}
class IconButtonApp extends StatelessWidget {
const IconButtonApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(colorSchemeSeed: const Color(0xff6750a4), useMaterial3: true),
title: 'Icon Button Types',
home: const Scaffold(
body: ButtonTypesExample(),
),
);
}
}
class ButtonTypesExample extends StatelessWidget {
const ButtonTypesExample({super.key});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: Row(
children: const <Widget>[
Spacer(),
ButtonTypesGroup(enabled: true),
ButtonTypesGroup(enabled: false),
Spacer(),
],
),
);
}
}
class ButtonTypesGroup extends StatelessWidget {
const ButtonTypesGroup({ super.key, required this.enabled });
final bool enabled;
@override
Widget build(BuildContext context) {
final VoidCallback? onPressed = enabled ? () {} : null;
final ColorScheme colors = Theme.of(context).colorScheme;
return Padding(
padding: const EdgeInsets.all(4.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(icon: const Icon(Icons.filter_drama), onPressed: onPressed),
// Use a standard IconButton with specific style to implement the
// 'Filled' type.
IconButton(
icon: const Icon(Icons.filter_drama),
onPressed: onPressed,
style: IconButton.styleFrom(
foregroundColor: colors.onPrimary,
backgroundColor: colors.primary,
disabledBackgroundColor: colors.onSurface.withOpacity(0.12),
hoverColor: colors.onPrimary.withOpacity(0.08),
focusColor: colors.onPrimary.withOpacity(0.12),
highlightColor: colors.onPrimary.withOpacity(0.12),
)
),
// Use a standard IconButton with specific style to implement the
// 'Filled Tonal' type.
IconButton(
icon: const Icon(Icons.filter_drama),
onPressed: onPressed,
style: IconButton.styleFrom(
foregroundColor: colors.onSecondaryContainer,
backgroundColor: colors.secondaryContainer,
disabledBackgroundColor: colors.onSurface.withOpacity(0.12),
hoverColor: colors.onSecondaryContainer.withOpacity(0.08),
focusColor: colors.onSecondaryContainer.withOpacity(0.12),
highlightColor: colors.onSecondaryContainer.withOpacity(0.12),
),
),
// Use a standard IconButton with specific style to implement the
// 'Outlined' type.
IconButton(
icon: const Icon(Icons.filter_drama),
onPressed: onPressed,
style: IconButton.styleFrom(
focusColor: colors.onSurfaceVariant.withOpacity(0.12),
highlightColor: colors.onSurface.withOpacity(0.12),
side: onPressed == null
? BorderSide(color: Theme.of(context).colorScheme.onSurface.withOpacity(0.12))
: BorderSide(color: colors.outline),
).copyWith(
foregroundColor: MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.pressed)) {
return colors.onSurface;
}
return null;
}),
),
),
],
),
);
}
}
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