blob: 783b7d0108adbab89b147f8f1897ff30da8e1846 [file] [log] [blame]
// 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 OptionModel 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) {
if (density == VisualDensity.standard) {
return 'standard';
} else if (density == VisualDensity.compact) {
return 'compact';
} else if (density == VisualDensity.comfortable) {
return 'comfortable';
}
return 'custom';
}
VisualDensity _profileToDensity(String? profile) {
switch (profile) {
case 'standard':
return VisualDensity.standard;
case 'comfortable':
return VisualDensity.comfortable;
case 'compact':
return VisualDensity.compact;
case 'custom':
default:
return 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 ThemeData 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 List<Widget> 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,
),
),
);
}),
),
),
),
),
),
);
}
}