| // 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. |
| |
| /// An example showing usage of [StarBorder]. |
| |
| import 'package:flutter/material.dart'; |
| |
| const int _kParameterPrecision = 2; |
| |
| void main() => runApp(const StarBorderApp()); |
| |
| class StarBorderApp extends StatelessWidget { |
| const StarBorderApp({super.key}); |
| |
| @override |
| Widget build(BuildContext context) { |
| return MaterialApp( |
| home: Scaffold( |
| appBar: AppBar( |
| title: const Text('StarBorder Example'), |
| backgroundColor: const Color(0xff323232), |
| ), |
| body: const StarBorderExample(), |
| ), |
| ); |
| } |
| } |
| |
| class StarBorderExample extends StatefulWidget { |
| const StarBorderExample({super.key}); |
| |
| @override |
| State<StarBorderExample> createState() => _StarBorderExampleState(); |
| } |
| |
| class _StarBorderExampleState extends State<StarBorderExample> { |
| final OptionModel _model = OptionModel(); |
| final TextEditingController _textController = TextEditingController(); |
| |
| @override |
| void initState() { |
| super.initState(); |
| _model.addListener(_modelChanged); |
| } |
| |
| @override |
| void dispose() { |
| _model.removeListener(_modelChanged); |
| _textController.dispose(); |
| super.dispose(); |
| } |
| |
| void _modelChanged() { |
| setState(() {}); |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return DefaultTextStyle( |
| style: const TextStyle( |
| color: Colors.black, |
| fontSize: 14.0, |
| fontFamily: 'Roboto', |
| fontStyle: FontStyle.normal, |
| ), |
| child: ListView( |
| children: <Widget>[ |
| Container( |
| color: Colors.grey.shade200, |
| child: Options(_model), |
| ), |
| Padding( |
| padding: const EdgeInsets.all(18.0), |
| child: Row( |
| mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
| children: <Widget>[ |
| Expanded( |
| child: ExampleBorder( |
| border: StarBorder( |
| side: const BorderSide(), |
| points: _model.points, |
| innerRadiusRatio: _model.innerRadiusRatio, |
| pointRounding: _model.pointRounding, |
| valleyRounding: _model.valleyRounding, |
| rotation: _model.rotation, |
| squash: _model.squash, |
| ), |
| title: 'Star', |
| ), |
| ), |
| Expanded( |
| child: ExampleBorder( |
| border: StarBorder.polygon( |
| side: const BorderSide(), |
| sides: _model.points, |
| pointRounding: _model.pointRounding, |
| rotation: _model.rotation, |
| squash: _model.squash, |
| ), |
| title: 'Polygon', |
| ), |
| ), |
| ], |
| ), |
| ), |
| Row( |
| crossAxisAlignment: CrossAxisAlignment.start, |
| children: <Widget>[ |
| Expanded( |
| child: Container( |
| color: Colors.black12, |
| margin: const EdgeInsets.all(16.0), |
| padding: const EdgeInsets.all(16.0), |
| child: SelectableText(_model.starCode), |
| ), |
| ), |
| Expanded( |
| child: Container( |
| color: Colors.black12, |
| margin: const EdgeInsets.all(16.0), |
| padding: const EdgeInsets.all(16.0), |
| child: SelectableText(_model.polygonCode), |
| ), |
| ), |
| ], |
| ), |
| ], |
| ), |
| ); |
| } |
| } |
| |
| class ExampleBorder extends StatelessWidget { |
| const ExampleBorder({ |
| super.key, |
| required this.border, |
| required this.title, |
| }); |
| |
| final StarBorder border; |
| final String title; |
| |
| @override |
| Widget build(BuildContext context) { |
| return Container( |
| key: UniqueKey(), |
| alignment: Alignment.center, |
| padding: const EdgeInsets.all(20), |
| width: 150, |
| height: 100, |
| decoration: ShapeDecoration( |
| color: Colors.blue.shade100, |
| shape: border, |
| ), |
| child: Text(title), |
| ); |
| } |
| } |
| |
| 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(() {}); |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return Padding( |
| padding: const EdgeInsets.fromLTRB(5.0, 0.0, 5.0, 10.0), |
| child: Column( |
| mainAxisSize: MainAxisSize.min, |
| children: <Widget>[ |
| Row( |
| children: <Widget>[ |
| Expanded( |
| child: ControlSlider( |
| label: 'Point Rounding', |
| value: widget.model.pointRounding, |
| onChanged: (double value) { |
| widget.model.pointRounding = value; |
| }, |
| ), |
| ), |
| Expanded( |
| child: ControlSlider( |
| label: 'Valley Rounding', |
| value: widget.model.valleyRounding, |
| onChanged: (double value) { |
| widget.model.valleyRounding = value; |
| }, |
| ), |
| ), |
| ], |
| ), |
| Row( |
| children: <Widget>[ |
| Expanded( |
| child: ControlSlider( |
| label: 'Squash', |
| value: widget.model.squash, |
| onChanged: (double value) { |
| widget.model.squash = value; |
| }, |
| ), |
| ), |
| Expanded( |
| child: ControlSlider( |
| label: 'Rotation', |
| value: widget.model.rotation, |
| max: 360, |
| onChanged: (double value) { |
| widget.model.rotation = value; |
| }, |
| ), |
| ), |
| ], |
| ), |
| Row( |
| children: <Widget>[ |
| Expanded( |
| child: Row( |
| children: <Widget>[ |
| Expanded( |
| child: ControlSlider( |
| label: 'Points', |
| value: widget.model.points, |
| min: 3, |
| max: 20, |
| precision: 1, |
| onChanged: (double value) { |
| widget.model.points = value; |
| }, |
| ), |
| ), |
| Tooltip( |
| message: 'Round the number of points to the nearest integer.', |
| child: Padding( |
| padding: const EdgeInsets.all(8.0), |
| child: OutlinedButton( |
| child: const Text('Nearest'), |
| onPressed: () { |
| widget.model.points = widget.model.points.roundToDouble(); |
| }, |
| ), |
| ), |
| ), |
| ], |
| ), |
| ), |
| Expanded( |
| child: ControlSlider( |
| label: 'Inner Radius', |
| value: widget.model.innerRadiusRatio, |
| onChanged: (double value) { |
| widget.model.innerRadiusRatio = value; |
| }, |
| ), |
| ), |
| ], |
| ), |
| ElevatedButton( |
| onPressed: () { |
| widget.model.reset(); |
| }, |
| child: const Text('Reset'), |
| ), |
| ], |
| ), |
| ); |
| } |
| } |
| |
| class OptionModel extends ChangeNotifier { |
| double get pointRounding => _pointRounding; |
| double _pointRounding = 0.0; |
| set pointRounding(double value) { |
| if (value != _pointRounding) { |
| _pointRounding = value; |
| if (_valleyRounding + _pointRounding > 1) { |
| _valleyRounding = 1.0 - _pointRounding; |
| } |
| notifyListeners(); |
| } |
| } |
| |
| double get valleyRounding => _valleyRounding; |
| double _valleyRounding = 0.0; |
| set valleyRounding(double value) { |
| if (value != _valleyRounding) { |
| _valleyRounding = value; |
| if (_valleyRounding + _pointRounding > 1) { |
| _pointRounding = 1.0 - _valleyRounding; |
| } |
| notifyListeners(); |
| } |
| } |
| |
| double get squash => _squash; |
| double _squash = 0.0; |
| set squash(double value) { |
| if (value != _squash) { |
| _squash = value; |
| notifyListeners(); |
| } |
| } |
| |
| double get rotation => _rotation; |
| double _rotation = 0.0; |
| set rotation(double value) { |
| if (value != _rotation) { |
| _rotation = value; |
| notifyListeners(); |
| } |
| } |
| |
| double get innerRadiusRatio => _innerRadiusRatio; |
| double _innerRadiusRatio = 0.4; |
| set innerRadiusRatio(double value) { |
| if (value != _innerRadiusRatio) { |
| _innerRadiusRatio = value.clamp(0.0001, double.infinity); |
| notifyListeners(); |
| } |
| } |
| |
| double get points => _points; |
| double _points = 5; |
| set points(double value) { |
| if (value != _points) { |
| _points = value; |
| notifyListeners(); |
| } |
| } |
| |
| String get starCode { |
| return 'Container(\n' |
| ' decoration: ShapeDecoration(\n' |
| ' shape: StarBorder(\n' |
| ' points: ${points.toStringAsFixed(_kParameterPrecision)},\n' |
| ' rotation: ${rotation.toStringAsFixed(_kParameterPrecision)},\n' |
| ' innerRadiusRatio: ${innerRadiusRatio.toStringAsFixed(_kParameterPrecision)},\n' |
| ' pointRounding: ${pointRounding.toStringAsFixed(_kParameterPrecision)},\n' |
| ' valleyRounding: ${valleyRounding.toStringAsFixed(_kParameterPrecision)},\n' |
| ' squash: ${squash.toStringAsFixed(_kParameterPrecision)},\n' |
| ' ),\n' |
| ' ),\n' |
| ');'; |
| } |
| |
| String get polygonCode { |
| return 'Container(\n' |
| ' decoration: ShapeDecoration(\n' |
| ' shape: StarBorder.polygon(\n' |
| ' sides: ${points.toStringAsFixed(_kParameterPrecision)},\n' |
| ' rotation: ${rotation.toStringAsFixed(_kParameterPrecision)},\n' |
| ' cornerRounding: ${pointRounding.toStringAsFixed(_kParameterPrecision)},\n' |
| ' squash: ${squash.toStringAsFixed(_kParameterPrecision)},\n' |
| ' ),\n' |
| ' ),\n' |
| ');'; |
| } |
| |
| void reset() { |
| final OptionModel defaultModel = OptionModel(); |
| _pointRounding = defaultModel.pointRounding; |
| _valleyRounding = defaultModel.valleyRounding; |
| _rotation = defaultModel.rotation; |
| _squash = defaultModel.squash; |
| _innerRadiusRatio = defaultModel._innerRadiusRatio; |
| _points = defaultModel.points; |
| notifyListeners(); |
| } |
| } |
| |
| class ControlSlider extends StatelessWidget { |
| const ControlSlider({ |
| super.key, |
| required this.label, |
| required this.value, |
| required this.onChanged, |
| this.min = 0.0, |
| this.max = 1.0, |
| this.precision = _kParameterPrecision, |
| }); |
| |
| final String label; |
| final double value; |
| final void Function(double value) onChanged; |
| final double min; |
| final double max; |
| final int precision; |
| |
| @override |
| Widget build(BuildContext context) { |
| return Padding( |
| padding: const EdgeInsets.all(4.0), |
| child: Row( |
| mainAxisSize: MainAxisSize.min, |
| children: <Widget>[ |
| Expanded( |
| flex: 2, |
| child: Text( |
| label, |
| textAlign: TextAlign.end, |
| ), |
| ), |
| Expanded( |
| flex: 5, |
| child: Slider( |
| onChanged: onChanged, |
| min: min, |
| max: max, |
| value: value, |
| ), |
| ), |
| Expanded( |
| child: Text( |
| value.toStringAsFixed(precision), |
| ), |
| ), |
| ], |
| ), |
| ); |
| } |
| } |