Created
February 3, 2022 11:34
-
-
Save Metal-666/68609884d53ab6d4d714d75385d23368 to your computer and use it in GitHub Desktop.
Challenge 4 (BLoC)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'package:flutter/material.dart'; | |
| import 'package:flutter_bloc/flutter_bloc.dart'; | |
| import 'dart:ui' as ui; | |
| import 'dart:math'; | |
| void main() => runApp(App()); | |
| //------------------------------Widgets----------------------------- | |
| class App extends StatelessWidget { | |
| final Color _primaryColor = Colors.deepPurple[700]!; | |
| final ThemeData _theme = ThemeData.dark(); | |
| @override | |
| Widget build(BuildContext context) => BlocProvider<RootBloc>( | |
| create: (BuildContext context) => RootBloc(), | |
| child: MaterialApp( | |
| theme: _theme.copyWith( | |
| colorScheme: _theme.colorScheme | |
| .copyWith(primary: _primaryColor, secondary: Colors.white), | |
| appBarTheme: AppBarTheme(color: _primaryColor)), | |
| home: Scaffold( | |
| appBar: _appBar(), | |
| body: SizedBox.expand( | |
| child: _root(), | |
| ), | |
| ), | |
| )); | |
| PreferredSizeWidget _appBar() => AppBar( | |
| title: const Text('Clock'), | |
| ); | |
| Widget _root() => Stack(children: <Widget>[ | |
| _fabs(<Widget>[_randomFAB(), const SizedBox(width: 10), _numbersFAB()]), | |
| _clock() | |
| ]); | |
| Widget _fabs(List<Widget> fabs) => | |
| Positioned(bottom: 10, right: 10, child: Row(children: fabs)); | |
| Widget _randomFAB() => Builder( | |
| builder: (context) => FloatingActionButton( | |
| onPressed: () => context.read<RootBloc>().add(RandomizeTime()), | |
| child: const Icon(Icons.shuffle), | |
| ), | |
| ); | |
| Widget _numbersFAB() => Builder( | |
| builder: (context) => FloatingActionButton( | |
| onPressed: () => context.read<RootBloc>().add(ToggleNumberStyle()), | |
| child: const Icon(Icons.refresh), | |
| ), | |
| ); | |
| Widget _clock() => | |
| Center(child: SizedBox(height: 300, width: 300, child: Clock())); | |
| } | |
| class Clock extends StatelessWidget { | |
| Widget _clockHand(double turns, double margin, double extent, | |
| double thiccness, Color handColor, Color dotColor) => | |
| AnimatedRotation( | |
| turns: turns, | |
| curve: Curves.easeInOutCubic, | |
| duration: const Duration(milliseconds: 500), | |
| child: CustomPaint( | |
| painter: ClockHandPainter( | |
| margin, extent, thiccness, handColor, dotColor))); | |
| @override | |
| Widget build(BuildContext context) => BlocBuilder<RootBloc, RootState>( | |
| builder: (BuildContext context, RootState state) => | |
| Stack(fit: StackFit.expand, children: <Widget>[ | |
| CustomPaint(painter: ClockPainter(state.numberStyle)), | |
| _clockHand(state.dateTime.second / 60, 30, 40, 3, | |
| Colors.deepPurple[500]!, Colors.white), | |
| _clockHand(state.dateTime.minute / 60, 40, 30, 3.5, Colors.white, | |
| Colors.deepPurple[800]!), | |
| _clockHand(state.dateTime.hour / 12, 60, 20, 4, Colors.white, | |
| Colors.deepPurple[800]!) | |
| ])); | |
| } | |
| class ClockPainter extends CustomPainter { | |
| final NumberStyle style; | |
| ClockPainter(this.style); | |
| @override | |
| void paint(Canvas canvas, Size size) { | |
| //Create our paints and colors | |
| final Paint background = Paint()..color = Colors.deepPurple[500]!, | |
| face = Paint() | |
| ..shader = ui.Gradient.linear( | |
| Offset.zero, | |
| Offset(size.width, size.height), | |
| [ | |
| Colors.deepPurple[600]!, | |
| Colors.deepPurple[900]!, | |
| ], | |
| ), | |
| bigMark = (Paint()..color = Colors.white)..strokeWidth = 2, | |
| smallMark = (Paint()..color = Colors.grey[400]!)..strokeWidth = 1; | |
| Color numbers = Colors.white; | |
| //Calculate some useful values | |
| final double radius = size.height / 2, | |
| centerX = size.width / 2, | |
| centerY = size.height / 2; | |
| //We might need this in the future? | |
| //canvas.clipRect(Rect.fromLTWH(0, 0, size.width, size.height)); | |
| //Draw clock background (outer circle) | |
| canvas.drawCircle(Offset(centerX, centerY), radius, background); | |
| //Draw clock face (inner circle) | |
| canvas.drawCircle(Offset(centerX, centerY), radius - 5, face); | |
| //Draw the dot in the middle (idk what it's called) | |
| canvas.drawCircle(Offset(centerX, centerY), 5, background); | |
| //Draw hour marks and minute marks | |
| _drawRadialMarks( | |
| 12, 13, 10, centerX, centerY, radius - 20, canvas, bigMark); | |
| _drawRadialMarks( | |
| 12 * 5, 5, 7, centerX, centerY, radius - 17, canvas, smallMark); | |
| //Draw numbers | |
| _drawRadialText( | |
| 12, centerX, centerY, radius - 35, _getClockNumbers(), canvas, numbers); | |
| } | |
| void _drawRadialMarks( | |
| int howMany, | |
| int skipEach, | |
| double length, | |
| double centerX, | |
| double centerY, | |
| double radius, | |
| Canvas canvas, | |
| Paint paint) { | |
| for (int i = 1; i <= howMany; i++) { | |
| if (i % skipEach != 0) { | |
| final double angle = i * (360 / howMany) * pi / 180; | |
| final double x1 = centerX + radius * cos(angle), | |
| y1 = centerY - radius * sin(angle); | |
| final double x2 = centerX + (radius + length) * cos(angle), | |
| y2 = centerY - (radius + length) * sin(angle); | |
| canvas.drawLine(Offset(x1, y1), Offset(x2, y2), paint); | |
| } | |
| } | |
| } | |
| void _drawRadialText(int howMany, double centerX, double centerY, | |
| double radius, List<String> strings, Canvas canvas, Color textColor) { | |
| final TextStyle style = TextStyle( | |
| color: textColor, | |
| fontSize: 20, | |
| ); | |
| for (int i = 0; i < howMany; i++) { | |
| final TextSpan text = TextSpan( | |
| text: strings.length > i ? strings[i] : 'uh', | |
| style: style, | |
| ); | |
| final TextPainter painter = TextPainter( | |
| text: text, | |
| textDirection: TextDirection.ltr, | |
| )..layout(maxWidth: 100, minWidth: 0); | |
| final double angle = (i * (360 / howMany) + 120) * pi / 180; | |
| final double x = centerX - radius * cos(angle), | |
| y = centerY - radius * sin(angle); | |
| painter.paint( | |
| canvas, Offset(x - painter.width / 2, y - painter.height / 2)); | |
| } | |
| } | |
| //Returns a set of numbers depending on current number style (roman|arabic) | |
| List<String> _getClockNumbers() { | |
| switch (style) { | |
| case NumberStyle.arabic: | |
| { | |
| return <String>[ | |
| '1', | |
| '2', | |
| '3', | |
| '4', | |
| '5', | |
| '6', | |
| '7', | |
| '8', | |
| '9', | |
| '10', | |
| '11', | |
| '12' | |
| ]; | |
| } | |
| case NumberStyle.roman: | |
| { | |
| return <String>[ | |
| 'I', | |
| 'II', | |
| 'III', | |
| 'IV', | |
| 'V', | |
| 'VI', | |
| 'VII', | |
| 'VIII', | |
| 'IX', | |
| 'X', | |
| 'XI', | |
| 'XII' | |
| ]; | |
| } | |
| default: | |
| { | |
| return <String>['Huh?']; | |
| } | |
| } | |
| } | |
| @override | |
| bool shouldRepaint(covariant ClockPainter oldDelegate) => | |
| oldDelegate.style != style; | |
| } | |
| class ClockHandPainter extends CustomPainter { | |
| final double margin; | |
| final double extent; | |
| final double thiccness; | |
| final Color handColor; | |
| final Color dotColor; | |
| const ClockHandPainter( | |
| this.margin, this.extent, this.thiccness, this.handColor, this.dotColor); | |
| @override | |
| void paint(Canvas canvas, Size size) { | |
| final Paint dotPaint = Paint()..color = dotColor, | |
| handPaint = (Paint()..color = handColor)..strokeWidth = thiccness; | |
| final double centerX = size.width / 2, centerY = size.height / 2; | |
| canvas.drawCircle(Offset(centerX, centerY), 6, handPaint); | |
| canvas.drawLine( | |
| Offset(centerX, centerY + extent), Offset(centerX, margin), handPaint); | |
| canvas.drawCircle(Offset(centerX, centerY), 3, dotPaint); | |
| } | |
| @override | |
| bool shouldRepaint(covariant CustomPainter oldDelegate) => true; | |
| } | |
| //------------------------------Widgets----------------------------- | |
| //------------------------------Events------------------------------ | |
| abstract class RootEvents {} | |
| class ToggleNumberStyle extends RootEvents {} | |
| class RandomizeTime extends RootEvents {} | |
| //------------------------------Events------------------------------ | |
| //------------------------------States------------------------------ | |
| class RootState { | |
| final NumberStyle numberStyle; | |
| final DateTime dateTime; | |
| RootState(this.numberStyle, this.dateTime); | |
| RootState changeNumberStyle(NumberStyle numberStyle) => | |
| RootState(numberStyle, dateTime); | |
| RootState changeDateTime(DateTime dateTime) => | |
| RootState(numberStyle, dateTime); | |
| } | |
| enum NumberStyle { roman, arabic } | |
| //------------------------------States------------------------------ | |
| //-------------------------------Bloc------------------------------- | |
| class RootBloc extends Bloc<RootEvents, RootState> { | |
| final Random random = Random(); | |
| RootBloc() : super(RootState(NumberStyle.roman, DateTime.now())) { | |
| //Toggle numberStyle | |
| on<ToggleNumberStyle>((event, emit) => emit(state.changeNumberStyle( | |
| state.numberStyle == NumberStyle.roman | |
| ? NumberStyle.arabic | |
| : NumberStyle.roman))); | |
| //Generate a new DateTime (from 0 milliseconds to half a day) | |
| on<RandomizeTime>((event, emit) { | |
| final int milliseconds = random.nextInt(1000 * 60 * 60 * 12); | |
| debugPrint('Generating time from milliseconds ($milliseconds)...'); | |
| emit(state | |
| .changeDateTime(DateTime.fromMillisecondsSinceEpoch(milliseconds))); | |
| debugPrint( | |
| 'Seconds: ${state.dateTime.second}, minutes: ${state.dateTime.minute}, hours: ${state.dateTime.hour}'); | |
| }); | |
| } | |
| } | |
| //-------------------------------Bloc------------------------------- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment