flutter/dev/tools/gen_defaults/lib/input_chip_template.dart
Taha Tesser 467c970bfb
Introduce MaterialState color property for chips (#128584)
fixes https://github.com/flutter/flutter/issues/115827
fixes https://github.com/flutter/flutter/issues/101325

### Description
1. This PR adds a new MaterialState `color` property to all the chips (this makes it possible to customize chips in all states from the M3 specs).
2. Updated defaults to use the new  MaterialState `color` property.
3. Updated and added new tests to all the chip test classes.

<details> 
<summary>code sample</summary> 

```dart
import 'package:flutter/material.dart';

const Color disabledColor = Colors.black26;
const Color backgroundColor = Colors.cyan;
final Color disabledSelectedColor = Colors.red.shade100;
const Color selectedColor = Colors.amber;
final MaterialStateProperty<Color> color =
    MaterialStateProperty.resolveWith((Set<MaterialState> states) {
  if (states.contains(MaterialState.disabled) &&
      states.contains(MaterialState.selected)) {
    return disabledSelectedColor;
  }
  if (states.contains(MaterialState.disabled)) {
    return disabledColor;
  }
  if (states.contains(MaterialState.selected)) {
    return selectedColor;
  }
  return backgroundColor;
});

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        // chipTheme: ChipThemeData(color: color),
      ),
      home: const Example(),
    );
  }
}

class Example extends StatefulWidget {
  const Example({super.key});

  @override
  State<Example> createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  bool enabled = false;
  bool selected = true;

  @override
  Widget build(BuildContext context) {
    const Widget verticalSpace = SizedBox(height: 20);

    return Scaffold(
      body: Center(
        child: Column(
          children: <Widget>[
            const SizedBox(height: 25),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                const Card(
                  elevation: 0.0,
                  color: disabledColor,
                  child: Padding(
                    padding: EdgeInsets.all(8.0),
                    child: Text('disabledColor'),
                  ),
                ),
                const Card(
                  elevation: 0.0,
                  color: backgroundColor,
                  child: Padding(
                    padding: EdgeInsets.all(8.0),
                    child: Text('backgroundColor'),
                  ),
                ),
                Card(
                  elevation: 0.0,
                  color: disabledSelectedColor,
                  child: const Padding(
                    padding: EdgeInsets.all(8.0),
                    child: Text('disabledSelectedColor'),
                  ),
                ),
                const Card(
                  elevation: 0.0,
                  color: selectedColor,
                  child: Padding(
                    padding: EdgeInsets.all(8.0),
                    child: Text('selectedColor'),
                  ),
                ),
              ],
            ),
            const Spacer(),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: <Widget>[
                Column(
                  mainAxisSize: MainAxisSize.min,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    RawChip(
                      selected: selected,
                      selectedColor: selectedColor,
                      color: color,
                      label: const Text('RawChip'),
                      isEnabled: enabled,
                      onSelected: enabled ? (bool value) {} : null,
                    ),
                    verticalSpace,
                    InputChip(
                      isEnabled: enabled,
                      selected: selected,
                      selectedColor: selectedColor,
                      color: color,
                      label: const Text('InputChip'),
                      onSelected: enabled ? (bool value) {} : null,
                    ),
                  ],
                ),
                Column(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    FilterChip(
                      selected: selected,
                      selectedColor: selectedColor,
                      color: color,
                      label: const Text('FilterChip'),
                      onSelected: enabled ? (bool value) {} : null,
                    ),
                    verticalSpace,
                    FilterChip.elevated(
                      selected: selected,
                      selectedColor: selectedColor,
                      color: color,
                      label: const Text('FilterChip.elevated'),
                      onSelected: enabled ? (bool value) {} : null,
                    ),
                  ],
                ),
                Column(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    ChoiceChip(
                      selected: selected,
                      selectedColor: selectedColor,
                      color: color,
                      label: const Text('ChoiceChip'),
                      onSelected: enabled ? (bool value) {} : null,
                    ),
                    verticalSpace,
                    ChoiceChip.elevated(
                      selected: selected,
                      selectedColor: selectedColor,
                      color: color,
                      label: const Text('ChoiceChip.elevated'),
                      onSelected: enabled ? (bool value) {} : null,
                    ),
                  ],
                ),
              ],
            ),
            const Spacer(),
            Row(
              children: <Widget>[
                Flexible(
                  child: SwitchListTile(
                    title: const Text('Enabled'),
                    value: enabled,
                    onChanged: (bool value) {
                      setState(() => enabled = value);
                    },
                  ),
                ),
                Flexible(
                  child: SwitchListTile(
                    title: const Text('Selected'),
                    value: selected,
                    onChanged: (bool value) {
                      setState(() => selected = value);
                    },
                  ),
                ),
              ],
            )
          ],
        ),
      ),
    );
  }
}

``` 
	
</details>

### Before (not possible to customize disabled and selected chips)

![Screenshot 2023-06-13 at 16 27 13](https://github.com/flutter/flutter/assets/48603081/633f09f7-16a1-469e-b326-b9cc0ed59242)

### After (using disabled and selected chips using the new  MaterialState `color` property)

![Screenshot 2023-06-13 at 16 26 53](https://github.com/flutter/flutter/assets/48603081/7f5dffb7-4074-4268-87c0-c059c2da67a8)
2023-06-19 22:03:26 +00:00

93 lines
3.3 KiB
Dart

// 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 InputChipTemplate extends TokenTemplate {
const InputChipTemplate(super.blockName, super.fileName, super.tokens, {
super.colorSchemePrefix = '_colors.',
super.textThemePrefix = '_textTheme.'
});
static const String tokenGroup = 'md.comp.input-chip';
static const String variant = '';
@override
String generate() => '''
class _${blockName}DefaultsM3 extends ChipThemeData {
_${blockName}DefaultsM3(this.context, this.isEnabled, this.isSelected)
: super(
elevation: ${elevation("$tokenGroup$variant.container")},
shape: ${shape("$tokenGroup.container")},
showCheckmark: true,
);
final BuildContext context;
final bool isEnabled;
final bool isSelected;
late final ColorScheme _colors = Theme.of(context).colorScheme;
late final TextTheme _textTheme = Theme.of(context).textTheme;
@override
TextStyle? get labelStyle => ${textStyle("$tokenGroup.label-text")};
@override
MaterialStateProperty<Color?>? get color =>
MaterialStateProperty.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.selected) && states.contains(MaterialState.disabled)) {
return ${componentColor("$tokenGroup$variant.disabled.selected.container")};
}
if (states.contains(MaterialState.disabled)) {
return ${componentColor("$tokenGroup$variant.disabled.container")};
}
if (states.contains(MaterialState.selected)) {
return ${componentColor("$tokenGroup$variant.selected.container")};
}
return ${componentColor("$tokenGroup$variant.container")};
});
@override
Color? get shadowColor => ${colorOrTransparent("$tokenGroup.container.shadow-color")};
@override
Color? get surfaceTintColor => ${colorOrTransparent("$tokenGroup.container.surface-tint-layer.color")};
@override
Color? get checkmarkColor => ${color("$tokenGroup.with-icon.selected.icon.color")};
@override
Color? get deleteIconColor => ${color("$tokenGroup.with-trailing-icon.selected.trailing-icon.color")};
@override
BorderSide? get side => !isSelected
? isEnabled
? ${border('$tokenGroup$variant.unselected.outline')}
: ${border('$tokenGroup$variant.disabled.unselected.outline')}
: const BorderSide(color: Colors.transparent);
@override
IconThemeData? get iconTheme => IconThemeData(
color: isEnabled
? ${color("$tokenGroup.with-leading-icon.leading-icon.color")}
: ${color("$tokenGroup.with-leading-icon.disabled.leading-icon.color")},
size: ${getToken("$tokenGroup.with-leading-icon.leading-icon.size")},
);
@override
EdgeInsetsGeometry? get padding => const EdgeInsets.all(8.0);
/// The chip at text scale 1 starts with 8px on each side and as text scaling
/// gets closer to 2 the label padding is linearly interpolated from 8px to 4px.
/// Once the widget has a text scaling of 2 or higher than the label padding
/// remains 4px.
@override
EdgeInsetsGeometry? get labelPadding => EdgeInsets.lerp(
const EdgeInsets.symmetric(horizontal: 8.0),
const EdgeInsets.symmetric(horizontal: 4.0),
clampDouble(MediaQuery.textScaleFactorOf(context) - 1.0, 0.0, 1.0),
)!;
}
''';
}