Kate Lovett 9d96df2364
Modernize framework lints (#179089)
WIP

Commits separated as follows:
- Update lints in analysis_options files
- Run `dart fix --apply`
- Clean up leftover analysis issues 
- Run `dart format .` in the right places.

Local analysis and testing passes. Checking CI now.

Part of https://github.com/flutter/flutter/issues/178827
- Adoption of flutter_lints in examples/api coming in a separate change
(cc @loic-sharma)

## Pre-launch Checklist

- [ ] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [ ] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [ ] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
2025-11-26 01:10:39 +00:00

623 lines
20 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 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show timeDilation;
final Map<int, Color> m2SwatchColors = <int, Color>{
50: const Color(0xfff2e7fe),
100: const Color(0xffd7b7fd),
200: const Color(0xffbb86fc),
300: const Color(0xff9e55fc),
400: const Color(0xff7f22fd),
500: const Color(0xff6200ee),
600: const Color(0xff4b00d1),
700: const Color(0xff3700b3),
800: const Color(0xff270096),
900: const Color(0xff270096),
};
final MaterialColor m2Swatch = MaterialColor(m2SwatchColors[500]!.value, m2SwatchColors);
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Density Test';
@override
Widget build(BuildContext context) {
return const MaterialApp(title: _title, home: MyHomePage());
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class OptionModel extends ChangeNotifier {
double get size => _size;
double _size = 1.0;
set size(double size) {
if (size != _size) {
_size = size;
notifyListeners();
}
}
VisualDensity get density => _density;
VisualDensity _density = VisualDensity.standard;
set density(VisualDensity density) {
if (density != _density) {
_density = density;
notifyListeners();
}
}
bool get enable => _enable;
bool _enable = true;
set enable(bool enable) {
if (enable != _enable) {
_enable = enable;
notifyListeners();
}
}
bool get slowAnimations => _slowAnimations;
bool _slowAnimations = false;
set slowAnimations(bool slowAnimations) {
if (slowAnimations != _slowAnimations) {
_slowAnimations = slowAnimations;
notifyListeners();
}
}
bool get rtl => _rtl;
bool _rtl = false;
set rtl(bool rtl) {
if (rtl != _rtl) {
_rtl = rtl;
notifyListeners();
}
}
bool get longText => _longText;
bool _longText = false;
void reset() {
final defaultModel = OptionModel();
_size = defaultModel.size;
_enable = defaultModel.enable;
_slowAnimations = defaultModel.slowAnimations;
_longText = defaultModel.longText;
_density = defaultModel.density;
_rtl = defaultModel.rtl;
notifyListeners();
}
}
class LabeledCheckbox extends StatelessWidget {
const LabeledCheckbox({super.key, required this.label, this.onChanged, this.value});
final String label;
final ValueChanged<bool?>? onChanged;
final bool? value;
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Checkbox(onChanged: onChanged, value: value),
Text(label),
],
);
}
}
class Options extends StatefulWidget {
const Options(this.model, {super.key});
final OptionModel model;
@override
State<Options> createState() => _OptionsState();
}
class _OptionsState extends State<Options> {
@override
void initState() {
super.initState();
widget.model.addListener(_modelChanged);
}
@override
void didUpdateWidget(Options oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.model != oldWidget.model) {
oldWidget.model.removeListener(_modelChanged);
widget.model.addListener(_modelChanged);
}
}
@override
void dispose() {
super.dispose();
widget.model.removeListener(_modelChanged);
}
void _modelChanged() {
setState(() {});
}
double sliderValue = 0.0;
String _densityToProfile(VisualDensity density) {
return switch (density) {
VisualDensity.standard => 'standard',
VisualDensity.compact => 'compact',
VisualDensity.comfortable => 'comfortable',
_ => 'custom',
};
}
VisualDensity _profileToDensity(String? profile) {
return switch (profile) {
'standard' => VisualDensity.standard,
'comfortable' => VisualDensity.comfortable,
'compact' => VisualDensity.compact,
'custom' || _ => widget.model.density,
};
}
@override
Widget build(BuildContext context) {
final SliderThemeData controlTheme = SliderTheme.of(context).copyWith(
thumbColor: Colors.grey[50],
activeTickMarkColor: Colors.deepPurple[200],
activeTrackColor: Colors.deepPurple[300],
inactiveTrackColor: Colors.grey[50],
);
return Padding(
padding: const EdgeInsets.fromLTRB(5.0, 0.0, 5.0, 10.0),
child: Builder(
builder: (BuildContext context) {
return DefaultTextStyle(
style: TextStyle(color: Colors.grey[50]),
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
const Text('Text Scale'),
Expanded(
child: SliderTheme(
data: controlTheme,
child: Slider(
label: '${widget.model.size}',
min: 0.5,
max: 3.0,
onChanged: (double value) {
widget.model.size = value;
},
value: widget.model.size,
),
),
),
Text(
widget.model.size.toStringAsFixed(3),
style: TextStyle(color: Colors.grey[50]),
),
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
const Text('X Density'),
Expanded(
child: SliderTheme(
data: controlTheme,
child: Slider(
label: widget.model.density.horizontal.toStringAsFixed(1),
min: VisualDensity.minimumDensity,
max: VisualDensity.maximumDensity,
onChanged: (double value) {
widget.model.density = widget.model.density.copyWith(
horizontal: value,
vertical: widget.model.density.vertical,
);
},
value: widget.model.density.horizontal,
),
),
),
Text(
widget.model.density.horizontal.toStringAsFixed(3),
style: TextStyle(color: Colors.grey[50]),
),
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: <Widget>[
const Text('Y Density'),
Expanded(
child: SliderTheme(
data: controlTheme,
child: Slider(
label: widget.model.density.vertical.toStringAsFixed(1),
min: VisualDensity.minimumDensity,
max: VisualDensity.maximumDensity,
onChanged: (double value) {
widget.model.density = widget.model.density.copyWith(
horizontal: widget.model.density.horizontal,
vertical: value,
);
},
value: widget.model.density.vertical,
),
),
),
Text(
widget.model.density.vertical.toStringAsFixed(3),
style: TextStyle(color: Colors.grey[50]),
),
],
),
),
Wrap(
alignment: WrapAlignment.center,
crossAxisAlignment: WrapCrossAlignment.center,
children: <Widget>[
Theme(
data: Theme.of(context).copyWith(canvasColor: Colors.grey[600]),
child: DropdownButton<String>(
style: TextStyle(color: Colors.grey[50]),
isDense: true,
onChanged: (String? value) {
widget.model.density = _profileToDensity(value);
},
items: const <DropdownMenuItem<String>>[
DropdownMenuItem<String>(value: 'standard', child: Text('Standard')),
DropdownMenuItem<String>(
value: 'comfortable',
child: Text('Comfortable'),
),
DropdownMenuItem<String>(value: 'compact', child: Text('Compact')),
DropdownMenuItem<String>(value: 'custom', child: Text('Custom')),
],
value: _densityToProfile(widget.model.density),
),
),
LabeledCheckbox(
label: 'Enabled',
onChanged: (bool? checked) {
widget.model.enable = checked ?? false;
},
value: widget.model.enable,
),
LabeledCheckbox(
label: 'Slow',
onChanged: (bool? checked) {
widget.model.slowAnimations = checked ?? false;
Future<void>.delayed(const Duration(milliseconds: 150)).then((_) {
if (widget.model.slowAnimations) {
timeDilation = 20.0;
} else {
timeDilation = 1.0;
}
});
},
value: widget.model.slowAnimations,
),
LabeledCheckbox(
label: 'RTL',
onChanged: (bool? checked) {
widget.model.rtl = checked ?? false;
},
value: widget.model.rtl,
),
MaterialButton(
onPressed: () {
widget.model.reset();
sliderValue = 0.0;
},
child: Text('Reset', style: TextStyle(color: Colors.grey[50])),
),
],
),
],
),
);
},
),
);
}
}
class _ControlTile extends StatelessWidget {
const _ControlTile({required this.label, required this.child});
final String label;
final Widget child;
@override
Widget build(BuildContext context) {
return Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
Align(
alignment: AlignmentDirectional.topStart,
child: Text(label, textAlign: TextAlign.start),
),
child,
],
),
),
);
}
}
class _MyHomePageState extends State<MyHomePage> {
static final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
final OptionModel _model = OptionModel();
final TextEditingController textController = TextEditingController();
@override
void initState() {
super.initState();
_model.addListener(_modelChanged);
}
@override
void dispose() {
super.dispose();
_model.removeListener(_modelChanged);
}
void _modelChanged() {
setState(() {});
}
double sliderValue = 0.0;
List<bool> checkboxValues = <bool>[false, false, false, false];
List<IconData> iconValues = <IconData>[Icons.arrow_back, Icons.play_arrow, Icons.arrow_forward];
List<String> chipValues = <String>['Potato', 'Computer'];
int radioValue = 0;
@override
Widget build(BuildContext context) {
final theme = ThemeData(primarySwatch: m2Swatch);
final Widget label = Text(_model.rtl ? 'اضغط علي' : 'Press Me');
textController.text = _model.rtl
? 'يعتمد القرار الجيد على المعرفة وليس على الأرقام.'
: 'A good decision is based on knowledge and not on numbers.';
final tiles = <Widget>[
_ControlTile(
label: _model.rtl ? 'حقل النص' : 'List Tile',
child: SizedBox(
width: 400,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ListTile(
title: Text(
_model.rtl ? 'هذا عنوان طويل نسبيا' : 'This is a relatively long title',
),
onTap: () {},
),
ListTile(
title: Text(_model.rtl ? 'هذا عنوان قصير' : 'This is a short title'),
subtitle: Text(
_model.rtl ? 'هذا عنوان فرعي مناسب.' : 'This is an appropriate subtitle.',
),
trailing: const Icon(Icons.check_box),
onTap: () {},
),
ListTile(
title: Text(_model.rtl ? 'هذا عنوان قصير' : 'This is a short title'),
subtitle: Text(
_model.rtl ? 'هذا عنوان فرعي مناسب.' : 'This is an appropriate subtitle.',
),
leading: const Icon(Icons.check_box),
dense: true,
onTap: () {},
),
ListTile(
title: Text(_model.rtl ? 'هذا عنوان قصير' : 'This is a short title'),
subtitle: Text(
_model.rtl ? 'هذا عنوان فرعي مناسب.' : 'This is an appropriate subtitle.',
),
dense: true,
leading: const Icon(Icons.add_box),
trailing: const Icon(Icons.check_box),
onTap: () {},
),
ListTile(
title: Text(_model.rtl ? 'هذا عنوان قصير' : 'This is a short title'),
subtitle: Text(
_model.rtl ? 'هذا عنوان فرعي مناسب.' : 'This is an appropriate subtitle.',
),
isThreeLine: true,
leading: const Icon(Icons.add_box),
trailing: const Icon(Icons.check_box),
onTap: () {},
),
],
),
),
),
_ControlTile(
label: _model.rtl ? 'حقل النص' : 'Text Field',
child: SizedBox(
width: 300,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(
controller: textController,
decoration: const InputDecoration(
hintText: 'Hint',
helperText: 'Helper',
labelText: 'Label',
border: OutlineInputBorder(),
),
),
TextField(controller: textController),
TextField(controller: textController, maxLines: 3),
],
),
),
),
_ControlTile(
label: _model.rtl ? 'رقائق' : 'Chips',
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: List<Widget>.generate(chipValues.length, (int index) {
return InputChip(
onPressed: _model.enable ? () {} : null,
onDeleted: _model.enable ? () {} : null,
label: Text(chipValues[index]),
deleteIcon: const Icon(Icons.delete),
avatar: const Icon(Icons.play_arrow),
);
}),
),
),
_ControlTile(
label: _model.rtl ? 'زر المواد' : 'Material Button',
child: MaterialButton(
color: m2Swatch[200],
onPressed: _model.enable ? () {} : null,
child: label,
),
),
_ControlTile(
label: _model.rtl ? 'زر مسطح' : 'Text Button',
child: TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: m2Swatch[200],
),
onPressed: _model.enable ? () {} : null,
child: label,
),
),
_ControlTile(
label: _model.rtl ? 'أثارت زر' : 'Elevated Button',
child: ElevatedButton(
style: TextButton.styleFrom(backgroundColor: m2Swatch[200]),
onPressed: _model.enable ? () {} : null,
child: label,
),
),
_ControlTile(
label: _model.rtl ? 'زر المخطط التفصيلي' : 'Outlined Button',
child: OutlinedButton(onPressed: _model.enable ? () {} : null, child: label),
),
_ControlTile(
label: _model.rtl ? 'خانات الاختيار' : 'Checkboxes',
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List<Widget>.generate(checkboxValues.length, (int index) {
return Checkbox(
onChanged: _model.enable
? (bool? value) {
setState(() {
checkboxValues[index] = value ?? false;
});
}
: null,
value: checkboxValues[index],
);
}),
),
),
_ControlTile(
label: _model.rtl ? 'زر الراديو' : 'Radio Button',
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List<Widget>.generate(4, (int index) {
return Radio<int>(
onChanged: _model.enable
? (int? value) {
setState(() {
radioValue = value!;
});
}
: null,
groupValue: radioValue,
value: index,
);
}),
),
),
_ControlTile(
label: _model.rtl ? 'زر الأيقونة' : 'Icon Button',
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List<Widget>.generate(iconValues.length, (int index) {
return IconButton(
onPressed: _model.enable ? () {} : null,
icon: Icon(iconValues[index]),
);
}),
),
),
];
return SafeArea(
child: Theme(
data: theme,
child: Scaffold(
key: scaffoldKey,
appBar: AppBar(
title: const Text('Density'),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(220.0),
child: Options(_model),
),
backgroundColor: const Color(0xff323232),
),
body: DefaultTextStyle(
style: const TextStyle(
color: Colors.black,
fontSize: 14.0,
fontFamily: 'Roboto',
fontStyle: FontStyle.normal,
),
child: Theme(
data: Theme.of(context).copyWith(visualDensity: _model.density),
child: Directionality(
textDirection: _model.rtl ? TextDirection.rtl : TextDirection.ltr,
child: Builder(
builder: (BuildContext context) {
final MediaQueryData mediaQueryData = MediaQuery.of(context);
return MediaQuery(
data: mediaQueryData.copyWith(textScaler: TextScaler.linear(_model.size)),
child: SizedBox.expand(child: ListView(children: tiles)),
);
},
),
),
),
),
),
),
);
}
}