From f8fe652eeffb1283fa3719264742f759eb5441ed Mon Sep 17 00:00:00 2001 From: Osman Date: Wed, 28 Feb 2024 12:11:20 +0000 Subject: [PATCH 1/2] Add Progress bar --- example/lib/home.dart | 18 +- .../pages/components/progress_example.dart | 130 ++++++++++++++ lib/src/components/progress/progress.dart | 85 +++++++++ lib/src/components/progress/progress_bar.dart | 168 ++++++++++++++++++ lib/zeta_flutter.dart | 1 + 5 files changed, 398 insertions(+), 4 deletions(-) create mode 100644 example/lib/pages/components/progress_example.dart create mode 100644 lib/src/components/progress/progress.dart create mode 100644 lib/src/components/progress/progress_bar.dart diff --git a/example/lib/home.dart b/example/lib/home.dart index aaafbe7b..19b41578 100644 --- a/example/lib/home.dart +++ b/example/lib/home.dart @@ -10,6 +10,8 @@ import 'package:zeta_example/pages/components/checkbox_example.dart'; import 'package:zeta_example/pages/components/chip_example.dart'; import 'package:zeta_example/pages/theme/color_example.dart'; import 'package:zeta_example/pages/components/password_input_example.dart'; +import 'package:zeta_example/pages/components/progress_example.dart'; + import 'package:zeta_example/pages/assets/icons_example.dart'; import 'package:zeta_example/widgets.dart'; import 'package:zeta_flutter/zeta_flutter.dart'; @@ -30,7 +32,9 @@ final List components = [ Component(ButtonExample.name, (context) => const ButtonExample()), Component(CheckBoxExample.name, (context) => const CheckBoxExample()), Component(ChipExample.name, (context) => const ChipExample()), - Component(PasswordInputExample.name, (context) => const PasswordInputExample()), + Component( + PasswordInputExample.name, (context) => const PasswordInputExample()), + Component(ProgressExample.name, (context) => const ProgressExample()) ]; final List theme = [ @@ -92,21 +96,27 @@ class _HomeState extends State { title: Text('Widgets'), backgroundColor: Zeta.of(context).colors.warm.shade30, children: _components - .map((item) => ListTile(title: Text(item.name), onTap: () => context.go('/${item.name}'))) + .map((item) => ListTile( + title: Text(item.name), + onTap: () => context.go('/${item.name}'))) .toList(), ), ExpansionTile( title: Text('Theme'), backgroundColor: Zeta.of(context).colors.warm.shade30, children: _theme - .map((item) => ListTile(title: Text(item.name), onTap: () => context.go('/${item.name}'))) + .map((item) => ListTile( + title: Text(item.name), + onTap: () => context.go('/${item.name}'))) .toList(), ), ExpansionTile( title: Text('Assets'), backgroundColor: Zeta.of(context).colors.warm.shade30, children: _assets - .map((item) => ListTile(title: Text(item.name), onTap: () => context.go('/${item.name}'))) + .map((item) => ListTile( + title: Text(item.name), + onTap: () => context.go('/${item.name}'))) .toList(), ), ], diff --git a/example/lib/pages/components/progress_example.dart b/example/lib/pages/components/progress_example.dart new file mode 100644 index 00000000..11ddae85 --- /dev/null +++ b/example/lib/pages/components/progress_example.dart @@ -0,0 +1,130 @@ +import 'package:flutter/material.dart'; +import 'package:zeta_example/widgets.dart'; +import 'package:zeta_flutter/zeta_flutter.dart'; + +class ProgressExample extends StatefulWidget { + static const String name = 'Progress'; + + const ProgressExample({super.key}); + + @override + State createState() => ProgressExampleState(); +} + +class ProgressExampleState extends State { + @override + Widget build(BuildContext context) { + return ExampleScaffold( + name: 'Progress', + child: Center( + child: SingleChildScrollView( + child: SizedBox( + width: double.infinity, + child: Column(children: [ + Wrapper( + stepsCompleted: 10, + weight: BarWeight.thin, + ), + SizedBox( + height: 20, + ), + Wrapper( + stepsCompleted: 0, + type: BarType.standard, + weight: BarWeight.thin, + stateChangeable: true), + SizedBox( + height: 20, + ), + Wrapper( + stepsCompleted: 0, + type: BarType.indeterminate, + weight: BarWeight.medium, + label: "UPLOADING ...", + ), + ]), + ), + ), + ), + ); + } +} + +class Wrapper extends StatefulWidget { + const Wrapper( + {super.key, + required this.stepsCompleted, + this.type = BarType.standard, + this.weight = BarWeight.medium, + this.border = ZetaWidgetBorder.rounded, + this.stateChangeable = false, + this.label}); + + final int stepsCompleted; + final ZetaWidgetBorder border; + final BarType type; + final BarWeight weight; + final String? label; + final bool stateChangeable; + + @override + State createState() => _WrapperState(); +} + +class _WrapperState extends State { + late int stepsCompleted; + late double progress; + late BarType type; + + @override + void initState() { + super.initState(); + type = widget.type; + stepsCompleted = widget.stepsCompleted; + progress = stepsCompleted / 10; + } + + ///Function to increase percentage of progress. + void increasePercentage() { + setState(() { + stepsCompleted++; + progress = stepsCompleted / 10; + }); + } + + void setLoading() { + setState(() { + type = type == BarType.buffering ? BarType.standard : BarType.buffering; + }); + } + + @override + Widget build(BuildContext context) { + return Column( + // Replace with a Column for vertical + children: [ + ProgressBar( + progress: progress, + border: widget.border, + type: type, + weight: widget.weight, + label: widget.label), + const SizedBox(width: 40), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + widget.type != BarType.indeterminate + ? FilledButton( + onPressed: increasePercentage, child: Text("Increase")) + : Container(), + const SizedBox(width: 40), + widget.stateChangeable + ? FilledButton( + onPressed: setLoading, child: Text("Start Buffering")) + : Container() + ], + ) + ], + ); + } +} diff --git a/lib/src/components/progress/progress.dart b/lib/src/components/progress/progress.dart new file mode 100644 index 00000000..278ab442 --- /dev/null +++ b/lib/src/components/progress/progress.dart @@ -0,0 +1,85 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Super class for [Progress] widgets. +/// Handles state for progress of [Progress] widgets. +abstract class Progress extends StatefulWidget { + /// Constructor for abstract [Progress] class. + const Progress({super.key, this.progress = 0}); + + /// Progress value, decimal value ranging from 0.0 - 1.0, 0.5 = 50% + final double progress; + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(DoubleProperty('progress', progress)); + } +} + +/// Super class for [ProgressState] +/// Defines functions that deal with state change of progress value and +/// animation changing. +abstract class ProgressState extends State + with TickerProviderStateMixin { + /// Decimal progress value + late double progress; + + ///Animation controller for [ProgressState] + late AnimationController controller; + + ///Animation for [ProgressState] + late Animation animation; + + @override + void initState() { + super.initState(); + progress = widget.progress; + controller = AnimationController( + vsync: this, duration: const Duration(milliseconds: 200)); + animation = Tween( + begin: widget.progress, // Start value + end: widget.progress, // End value (initially same as start value) + ).animate(controller) + ..addListener(() { + setState(() { + progress = animation.value; + }); + }); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + ///Update animation with new progress value to give animation effect for progress changing. + void updateProgress(double newProgress) { + // Update the Tween with new start and end value + + setState(() { + animation = Tween( + begin: progress, + end: newProgress, + ).animate(controller); + controller.forward(from: progress); + }); + } + + @override + void didUpdateWidget(T oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.progress != widget.progress) { + updateProgress(widget.progress); + } + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DoubleProperty('progress', progress)) + ..add(DiagnosticsProperty('controller', controller)) + ..add(DiagnosticsProperty>('animation', animation)); + } +} diff --git a/lib/src/components/progress/progress_bar.dart b/lib/src/components/progress/progress_bar.dart new file mode 100644 index 00000000..38b3e262 --- /dev/null +++ b/lib/src/components/progress/progress_bar.dart @@ -0,0 +1,168 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import '../../../zeta_flutter.dart'; +import 'progress.dart'; + +/// Enum for types of progress bar +enum BarType { + /// Standard bar type. + standard, + + /// Indeterminate bar type. positions randomly + indeterminate, + + /// Buffering bar type. Has buffering at end. + buffering +} + +/// BarWeight of progress bar +enum BarWeight { + /// Thin weight. + thin, + + ///Medium weight + medium +} + +/// Linear progress bar. +/// Uses progress percentage value to fill bar +class ProgressBar extends Progress { + ///Constructor for [ProgressBar] + + const ProgressBar( + {super.key, + required super.progress, + required this.border, + required this.type, + required this.weight, + this.label}); + + /// Constructs a standard progress bar + const ProgressBar.standard( + {super.key, + required super.progress, + this.border = ZetaWidgetBorder.rounded, + this.type = BarType.standard, + this.weight = BarWeight.medium, + this.label}); + + /// Constructs buffering example + const ProgressBar.buffering( + {super.key, + required super.progress, + this.border = ZetaWidgetBorder.rounded, + this.type = BarType.buffering, + this.weight = BarWeight.medium, + this.label}); + + /// Constructs indeterminate example + const ProgressBar.indeterminate( + {super.key, + required super.progress, + this.border = ZetaWidgetBorder.rounded, + this.type = BarType.indeterminate, + this.weight = BarWeight.medium, + this.label}); + + /// Border type of the progress bar. + final ZetaWidgetBorder border; + + /// Type of the progress bar. + final BarType type; + + /// Weight of progress bar to determine its thickness. + final BarWeight weight; + + ///Optional label + final String? label; + + @override + _ProgressBarState createState() => _ProgressBarState(); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(EnumProperty('border', border)) + ..add(EnumProperty('type', type)) + ..add(EnumProperty('weight', weight)) + ..add(StringProperty('label', label)); + } +} + +class _ProgressBarState extends ProgressState { + @override + Widget build(BuildContext context) { + return SizedBox( + width: 400, + child: Column( + children: [ + if (widget.label != null) + SizedBox( + child: Text( + widget.label!, + textAlign: TextAlign.start, + ), + ), + Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Expanded( + child: AnimatedContainer( + duration: const Duration(milliseconds: 500), + height: _weight(), + child: LinearProgressIndicator( + borderRadius: _border(), + value: widget.type == BarType.indeterminate + ? null + : animation.value, + backgroundColor: widget.type == BarType.buffering + ? const Color.fromRGBO(224, 227, 233, 1) + : Colors.transparent, + ), + ), + ), + _extraWidgets(), + ]) + ], + ), + ); + } + + /// Returns border based on widgets border type. + BorderRadius _border() { + return BorderRadius.all(widget.border == ZetaWidgetBorder.rounded + ? const Radius.circular(12) + : Radius.zero); + } + + /// Returns thickness of progress bar based on its weight. + double _weight() { + switch (widget.weight) { + case BarWeight.medium: + return 16; + case BarWeight.thin: + return 8; + } + } + + Widget _extraWidgets() { + final Iterable> extraList = List.filled(3, false).map((e) => [ + const SizedBox( + width: 16, + ), + Container( + width: _weight(), + height: _weight(), + decoration: const BoxDecoration( + color: Color.fromRGBO(224, 227, 233, 1), + borderRadius: ZetaRadius.rounded), + ), + ]); + + final Widget extraWidgets = Row( + children: widget.type == BarType.buffering + ? extraList.expand((list) => list).toList() + : [], + ); + return extraWidgets; + } +} diff --git a/lib/zeta_flutter.dart b/lib/zeta_flutter.dart index eb763dca..a3a386f8 100644 --- a/lib/zeta_flutter.dart +++ b/lib/zeta_flutter.dart @@ -19,6 +19,7 @@ export 'src/components/buttons/fab.dart'; export 'src/components/checkbox/checkbox.dart'; export 'src/components/chips/chip.dart'; export 'src/components/password/password_input.dart'; +export 'src/components/progress/progress_bar.dart'; export 'src/theme/color_extensions.dart'; export 'src/theme/color_scheme.dart'; export 'src/theme/color_swatch.dart'; From 87e9d715dd714124f61fbc9f2956656ff1a4ab72 Mon Sep 17 00:00:00 2001 From: Osman Date: Wed, 28 Feb 2024 13:51:58 +0000 Subject: [PATCH 2/2] Remove fixed with for progress bar --- .../pages/components/progress_example.dart | 15 +++--- lib/src/components/progress/progress_bar.dart | 53 +++++++++---------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/example/lib/pages/components/progress_example.dart b/example/lib/pages/components/progress_example.dart index 11ddae85..69b31456 100644 --- a/example/lib/pages/components/progress_example.dart +++ b/example/lib/pages/components/progress_example.dart @@ -103,12 +103,15 @@ class _WrapperState extends State { return Column( // Replace with a Column for vertical children: [ - ProgressBar( - progress: progress, - border: widget.border, - type: type, - weight: widget.weight, - label: widget.label), + SizedBox( + width: 400, + child: ProgressBar( + progress: progress, + border: widget.border, + type: type, + weight: widget.weight, + label: widget.label), + ), const SizedBox(width: 40), Row( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/src/components/progress/progress_bar.dart b/lib/src/components/progress/progress_bar.dart index 38b3e262..488ed621 100644 --- a/lib/src/components/progress/progress_bar.dart +++ b/lib/src/components/progress/progress_bar.dart @@ -93,37 +93,34 @@ class ProgressBar extends Progress { class _ProgressBarState extends ProgressState { @override Widget build(BuildContext context) { - return SizedBox( - width: 400, - child: Column( - children: [ - if (widget.label != null) - SizedBox( - child: Text( - widget.label!, - textAlign: TextAlign.start, - ), + return Column( + children: [ + if (widget.label != null) + SizedBox( + child: Text( + widget.label!, + textAlign: TextAlign.start, ), - Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - child: AnimatedContainer( - duration: const Duration(milliseconds: 500), - height: _weight(), - child: LinearProgressIndicator( - borderRadius: _border(), - value: widget.type == BarType.indeterminate - ? null - : animation.value, - backgroundColor: widget.type == BarType.buffering - ? const Color.fromRGBO(224, 227, 233, 1) - : Colors.transparent, - ), + ), + Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Expanded( + child: AnimatedContainer( + duration: const Duration(milliseconds: 500), + height: _weight(), + child: LinearProgressIndicator( + borderRadius: _border(), + value: widget.type == BarType.indeterminate + ? null + : animation.value, + backgroundColor: widget.type == BarType.buffering + ? const Color.fromRGBO(224, 227, 233, 1) + : Colors.transparent, ), ), - _extraWidgets(), - ]) - ], - ), + ), + _extraWidgets(), + ]) + ], ); }