├── .gitignore
├── LICENSE
├── README.md
├── flutter_clock_helper
├── .metadata
├── CHANGELOG.md
├── README.md
├── customizer.png
├── lib
│ ├── customizer.dart
│ └── model.dart
└── pubspec.yaml
├── iso_clock
├── .gitignore
├── .metadata
├── README.md
├── lib
│ ├── digit_data.dart
│ ├── iso_clock.dart
│ └── main.dart
├── pubspec.lock
└── pubspec.yaml
├── iso_clock_dark_theme.gif
├── iso_clock_light_theme.gif
└── iso_clock_scott_cook.zip
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Build stuff
24 | **/build/
25 |
26 | # Flutter/Dart/Pub related
27 | **/doc/api/
28 | .dart_tool/
29 | .flutter-plugins
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | /build/
34 |
35 | # Web related
36 | lib/generated_plugin_registrant.dart
37 |
38 | # Other items since we are using a global .gitignore.
39 | **/.dart_tool
40 | **/.pubspec.lock
41 | **/.flutter-plugins
42 | **/.packages
43 | **/.pub-cache/
44 | **/.pub/
45 | **/build/
46 |
47 | # Remove pubspec.lock for libraries
48 | flutter_clock_helper/pubspec.lock
49 |
50 | # Other items grabbed from flutter/flutter repo
51 | flutter_*.png
52 | linked_*.ds
53 | unlinked.ds
54 | unlinked_spec.ds
55 |
56 | # Android related
57 | **/android
58 | **/android/**/gradle-wrapper.jar
59 | **/android/.gradle
60 | **/android/captures/
61 | **/android/gradlew
62 | **/android/gradlew.bat
63 | **/android/local.properties
64 | **/android/**/GeneratedPluginRegistrant.java
65 | **/android/key.properties
66 | *.jks
67 |
68 | # iOS/XCode related
69 | **/ios
70 | **/ios/**/*.mode1v3
71 | **/ios/**/*.mode2v3
72 | **/ios/**/*.moved-aside
73 | **/ios/**/*.pbxuser
74 | **/ios/**/*.perspectivev3
75 | **/ios/**/*sync/
76 | **/ios/**/.sconsign.dblite
77 | **/ios/**/.tags*
78 | **/ios/**/.vagrant/
79 | **/ios/**/DerivedData/
80 | **/ios/**/Icon?
81 | **/ios/**/Pods/
82 | **/ios/**/.symlinks/
83 | **/ios/**/profile
84 | **/ios/**/xcuserdata
85 | **/ios/.generated/
86 | **/ios/Flutter/App.framework
87 | **/ios/Flutter/Flutter.framework
88 | **/ios/Flutter/Flutter.podspec
89 | **/ios/Flutter/Generated.xcconfig
90 | **/ios/Flutter/app.flx
91 | **/ios/Flutter/app.zip
92 | **/ios/Flutter/flutter_assets/
93 | **/ios/Flutter/flutter_export_environment.sh
94 | **/ios/ServiceDefinitions.json
95 | **/ios/Runner/GeneratedPluginRegistrant.*
96 |
97 | # Exceptions to above rules.
98 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
99 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2019 The Chromium Authors. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without modification,
4 | are permitted provided that the following conditions are met:
5 |
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above
9 | copyright notice, this list of conditions and the following
10 | disclaimer in the documentation and/or other materials provided
11 | with the distribution.
12 | * Neither the name of Google Inc. nor the names of its
13 | contributors may be used to endorse or promote products derived
14 | from this software without specific prior written permission.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Iso Clock - Flutter Challenge
2 |
3 | This is my take on a classic digital clock look with a modern 3D "flat" design and playful animations. I come from a web background and this is my first Flutter App, so go easy! It has been an absolute delight working with Flutter and I'm excited to encorporate it into future projects.
4 |
5 |
6 |
7 |
8 | **Design Decisions...**
9 | - The Digit faces use color gradients to invoke more playful depth
10 | - I kept this clock a clock, weather and dates on screen were visually distracting
11 | - Digits are large and in charge for readability
12 | - Lay flat diametric digits look super cool, but upright isometric design had better readability
13 | - Dark mode is very dark while keeping enough contrast so light emission is low at night
14 |
15 | **Things I've learned...**
16 | - dartpad.dev is a-m-a-z-i-n-g for sketch-ups and quick prototyping of ideas
17 | - `Stack` Class's `Overflow.clip` does not work on transformed children per https://github.com/flutter/flutter/issues/40216-
18 | - Running in profile mode on your device with performance monitor is perfect for testing "real-life" app performance quickly
19 | - Flutter can take a lot you throw at it, so I had to be careful that I used best practices
20 |
21 | **Anti-aliasiang Artifacts Workaround...**
22 |
23 | The `ClipRect` Class's anti-aliasing technique causes artifacts on shape's borders with fractional coordinates. This can be seen in this flutter [issue](https://github.com/flutter/flutter/issues/25009). The edges of the digits in my app were experiencing this issue, and it was pertinent to the design that edges were flush and continuous with the backaground.
24 |
25 | To work around this issue, I essentially put a visual band-aid on top of the artifacts by:
26 | - wrap the main Digit face (DigitFacade) with a parent container
27 | - scale down the size of the Digit face so the perimeter does not share sides with parent
28 | - add an inset border overlay on top of the digit facade that covers the artifacts created by the anti-aliasing algorithm
29 |
--------------------------------------------------------------------------------
/flutter_clock_helper/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 9bd02a1787bd264fde13a445a900ca28d08e3cef
8 | channel: master
9 |
10 | project_type: package
11 |
--------------------------------------------------------------------------------
/flutter_clock_helper/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [0.0.1] - TODO: Add release date.
2 |
3 | * TODO: Describe initial release.
4 |
--------------------------------------------------------------------------------
/flutter_clock_helper/README.md:
--------------------------------------------------------------------------------
1 | # Flutter Clock Helper
2 |
3 | This package provides scaffolding code for the clock in the Flutter Clock contest.
4 |
5 | Contestants: Do not edit this code.
6 |
7 |
8 | ## Model
9 | Provides data that can change in the clock. Look in model.dart for more details.
10 |
11 | * Time format (12- or 24-hour)
12 | * Location
13 | * Temperature
14 | * Temperature high
15 | * Temperature low
16 | * Temperature unit
17 | * Weather unit
18 |
19 |
20 | ## Clock Customizer
21 | Provides customizations for your clock (based on the model).
22 | You can change the behavior of your clock based on these customizations.
23 |
24 |
25 |
26 | To use inside your app's `main.dart`:
27 |
28 | ```
29 | runApp(ClockCustomizer((ClockModel model) => AnalogClock(model)));
30 | ```
31 |
32 | For more information, see the code inside [lib/](lib).
33 | For a full example, see the [Analog Clock](../analog_clock) or [Digital Clock](../digital_clock) in this GitHub repo.
34 |
--------------------------------------------------------------------------------
/flutter_clock_helper/customizer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cookmscott/FlutterClockChallenge/03cd17593f7734517582b0c5b78e919e2cd0b541/flutter_clock_helper/customizer.png
--------------------------------------------------------------------------------
/flutter_clock_helper/lib/customizer.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2019 The Chromium Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be
3 | // found in the LICENSE file.
4 |
5 | import 'package:flutter/material.dart';
6 |
7 | import 'model.dart';
8 |
9 | /// Returns a clock [Widget] with [ClockModel].
10 | ///
11 | /// Example:
12 | /// final myClockBuilder = (ClockModel model) => AnalogClock(model);
13 | ///
14 | /// Contestants: Do not edit this.
15 | typedef Widget ClockBuilder(ClockModel model);
16 |
17 | /// Wrapper for clock widget to allow for customizations.
18 | ///
19 | /// Puts the clock in landscape orientation with an aspect ratio of 5:3.
20 | /// Provides a drawer where users can customize the data that is sent to the
21 | /// clock. To show/hide the drawer, double-tap the clock.
22 | ///
23 | /// To use the [ClockCustomizer], pass your clock into it, using a ClockBuilder.
24 | ///
25 | /// ```
26 | /// final myClockBuilder = (ClockModel model) => AnalogClock(model);
27 | /// return ClockCustomizer(myClockBuilder);
28 | /// ```
29 | /// Contestants: Do not edit this.
30 | class ClockCustomizer extends StatefulWidget {
31 | const ClockCustomizer(this._clock);
32 |
33 | /// The clock widget with [ClockModel], to update and display.
34 | final ClockBuilder _clock;
35 |
36 | @override
37 | _ClockCustomizerState createState() => _ClockCustomizerState();
38 | }
39 |
40 | class _ClockCustomizerState extends State {
41 | final _model = ClockModel();
42 | ThemeMode _themeMode = ThemeMode.light;
43 | bool _configButtonShown = false;
44 |
45 | @override
46 | void initState() {
47 | super.initState();
48 | _model.addListener(_handleModelChange);
49 | }
50 |
51 | @override
52 | void dispose() {
53 | _model.removeListener(_handleModelChange);
54 | _model.dispose();
55 | super.dispose();
56 | }
57 |
58 | void _handleModelChange() => setState(() {});
59 |
60 | Widget _enumMenu(
61 | String label, T value, List items, ValueChanged onChanged) {
62 | return InputDecorator(
63 | decoration: InputDecoration(
64 | labelText: label,
65 | ),
66 | child: DropdownButtonHideUnderline(
67 | child: DropdownButton(
68 | value: value,
69 | isDense: true,
70 | onChanged: onChanged,
71 | items: items.map((T item) {
72 | return DropdownMenuItem(
73 | value: item,
74 | child: Text(enumToString(item)),
75 | );
76 | }).toList(),
77 | ),
78 | ),
79 | );
80 | }
81 |
82 | Widget _switch(String label, bool value, ValueChanged onChanged) {
83 | return Row(
84 | children: [
85 | Expanded(child: Text(label)),
86 | Switch(
87 | value: value,
88 | onChanged: onChanged,
89 | ),
90 | ],
91 | );
92 | }
93 |
94 | Widget _textField(
95 | String currentValue, String label, ValueChanged onChanged) {
96 | return TextField(
97 | decoration: InputDecoration(
98 | hintText: currentValue,
99 | helperText: label,
100 | ),
101 | onChanged: onChanged,
102 | );
103 | }
104 |
105 | Widget _configDrawer(BuildContext context) {
106 | return SafeArea(
107 | child: Drawer(
108 | child: Padding(
109 | padding: const EdgeInsets.all(16.0),
110 | child: SingleChildScrollView(
111 | child: Column(
112 | children: [
113 | _textField(_model.location, 'Location', (String location) {
114 | setState(() {
115 | _model.location = location;
116 | });
117 | }),
118 | _textField(_model.temperature.toString(), 'Temperature',
119 | (String temperature) {
120 | setState(() {
121 | _model.temperature = double.parse(temperature);
122 | });
123 | }),
124 | _enumMenu('Theme', _themeMode,
125 | ThemeMode.values.toList()..remove(ThemeMode.system),
126 | (ThemeMode mode) {
127 | setState(() {
128 | _themeMode = mode;
129 | });
130 | }),
131 | _switch('24-hour format', _model.is24HourFormat, (bool value) {
132 | setState(() {
133 | _model.is24HourFormat = value;
134 | });
135 | }),
136 | _enumMenu(
137 | 'Weather', _model.weatherCondition, WeatherCondition.values,
138 | (WeatherCondition condition) {
139 | setState(() {
140 | _model.weatherCondition = condition;
141 | });
142 | }),
143 | _enumMenu('Units', _model.unit, TemperatureUnit.values,
144 | (TemperatureUnit unit) {
145 | setState(() {
146 | _model.unit = unit;
147 | });
148 | }),
149 | ],
150 | ),
151 | ),
152 | ),
153 | ),
154 | );
155 | }
156 |
157 | Widget _configButton() {
158 | return Builder(
159 | builder: (BuildContext context) {
160 | return IconButton(
161 | icon: Icon(Icons.settings),
162 | tooltip: 'Configure clock',
163 | onPressed: () {
164 | Scaffold.of(context).openEndDrawer();
165 | setState(() {
166 | _configButtonShown = false;
167 | });
168 | },
169 | );
170 | },
171 | );
172 | }
173 |
174 | @override
175 | Widget build(BuildContext context) {
176 | final clock = Center(
177 | child: AspectRatio(
178 | aspectRatio: 5 / 3,
179 | child: Container(
180 | decoration: BoxDecoration(
181 | border: Border.all(
182 | width: 2,
183 | color: Theme.of(context).unselectedWidgetColor,
184 | ),
185 | ),
186 | child: widget._clock(_model),
187 | ),
188 | ),
189 | );
190 |
191 | return MaterialApp(
192 | theme: ThemeData.light(),
193 | darkTheme: ThemeData.dark(),
194 | themeMode: _themeMode,
195 | debugShowCheckedModeBanner: false,
196 | home: Scaffold(
197 | resizeToAvoidBottomPadding: false,
198 | endDrawer: _configDrawer(context),
199 | body: SafeArea(
200 | child: GestureDetector(
201 | behavior: HitTestBehavior.opaque,
202 | onTap: () {
203 | setState(() {
204 | _configButtonShown = !_configButtonShown;
205 | });
206 | },
207 | child: Stack(
208 | children: [
209 | clock,
210 | if (_configButtonShown)
211 | Positioned(
212 | top: 0,
213 | right: 0,
214 | child: Opacity(
215 | opacity: 0.7,
216 | child: _configButton(),
217 | ),
218 | ),
219 | ],
220 | ),
221 | ),
222 | ),
223 | ),
224 | );
225 | }
226 | }
227 |
--------------------------------------------------------------------------------
/flutter_clock_helper/lib/model.dart:
--------------------------------------------------------------------------------
1 | // Copyright 2019 The Chromium Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be
3 | // found in the LICENSE file.
4 |
5 | import 'package:flutter/foundation.dart';
6 | import 'package:flutter/material.dart';
7 |
8 | /// This is the model that contains the customization options for the clock.
9 | ///
10 | /// It is a [ChangeNotifier], so use [ChangeNotifier.addListener] to listen to
11 | /// changes to the model. Be sure to call [ChangeNotifier.removeListener] in
12 | /// your `dispose` method.
13 | ///
14 | /// Contestants: Do not edit this.
15 | class ClockModel extends ChangeNotifier {
16 | get is24HourFormat => _is24HourFormat;
17 | bool _is24HourFormat = true;
18 | set is24HourFormat(bool is24HourFormat) {
19 | if (_is24HourFormat != is24HourFormat) {
20 | _is24HourFormat = is24HourFormat;
21 | notifyListeners();
22 | }
23 | }
24 |
25 | /// Current location String, for example 'Mountain View, CA'.
26 | get location => _location;
27 | String _location = 'Mountain View, CA';
28 | set location(String location) {
29 | if (location != _location) {
30 | _location = location;
31 | notifyListeners();
32 | }
33 | }
34 |
35 | /// Current temperature string, for example '22°C'.
36 | get temperature => _convertFromCelsius(_temperature);
37 | // Stored in degrees celsius, and converted based on the current unit setting
38 | num _temperature = 22.0;
39 | set temperature(num temperature) {
40 | temperature = _convertToCelsius(temperature);
41 | if (temperature != _temperature) {
42 | _temperature = temperature;
43 | _low = _temperature - 3.0;
44 | _high = _temperature + 4.0;
45 | notifyListeners();
46 | }
47 | }
48 |
49 | /// Daily high temperature, for example '26'.
50 | get high => _convertFromCelsius(_high);
51 | // Stored in degrees celsius, and converted based on the current unit setting
52 | num _high = 26.0;
53 | set high(num high) {
54 | high = _convertToCelsius(high);
55 | if (high != _high) {
56 | _high = high;
57 | notifyListeners();
58 | }
59 | }
60 |
61 | /// Daily low temperature, for example '19'.
62 | get low => _convertFromCelsius(_low);
63 | num _low = 19.0;
64 | set low(num low) {
65 | low = _convertToCelsius(low);
66 | if (low != _low) {
67 | _low = low;
68 | notifyListeners();
69 | }
70 | }
71 |
72 | /// Weather condition text for the current weather, for example 'cloudy'.
73 | WeatherCondition get weatherCondition => _weatherCondition;
74 | WeatherCondition _weatherCondition = WeatherCondition.sunny;
75 | set weatherCondition(WeatherCondition weatherCondition) {
76 | if (weatherCondition != _weatherCondition) {
77 | _weatherCondition = weatherCondition;
78 | notifyListeners();
79 | }
80 | }
81 |
82 | /// [WeatherCondition] value without the enum type.
83 | String get weatherString => enumToString(weatherCondition);
84 |
85 | /// Temperature unit, for example 'celsius'.
86 | TemperatureUnit get unit => _unit;
87 | TemperatureUnit _unit = TemperatureUnit.celsius;
88 | set unit(TemperatureUnit unit) {
89 | if (unit != _unit) {
90 | _unit = unit;
91 | notifyListeners();
92 | }
93 | }
94 |
95 | /// Temperature with unit of measurement.
96 | String get temperatureString {
97 | return '${temperature.toStringAsFixed(1)}$unitString';
98 | }
99 |
100 | /// Temperature high with unit of measurement.
101 | String get highString {
102 | return '${high.toStringAsFixed(1)}$unitString';
103 | }
104 |
105 | /// Temperature low with unit of measurement.
106 | String get lowString {
107 | return '${low.toStringAsFixed(1)}$unitString';
108 | }
109 |
110 | /// Temperature unit of measurement with degrees.
111 | String get unitString {
112 | switch (unit) {
113 | case TemperatureUnit.fahrenheit:
114 | return '°F';
115 | case TemperatureUnit.celsius:
116 | default:
117 | return '°C';
118 | }
119 | }
120 |
121 | num _convertFromCelsius(num degreesCelsius) {
122 | switch (unit) {
123 | case TemperatureUnit.fahrenheit:
124 | return 32.0 + degreesCelsius * 9.0 / 5.0;
125 | case TemperatureUnit.celsius:
126 | default:
127 | return degreesCelsius;
128 | break;
129 | }
130 | }
131 |
132 | num _convertToCelsius(num degrees) {
133 | switch (unit) {
134 | case TemperatureUnit.fahrenheit:
135 | return (degrees - 32.0) * 5.0 / 9.0;
136 | case TemperatureUnit.celsius:
137 | default:
138 | return degrees;
139 | break;
140 | }
141 | }
142 | }
143 |
144 | /// Weather condition in English.
145 | enum WeatherCondition {
146 | cloudy,
147 | foggy,
148 | rainy,
149 | snowy,
150 | sunny,
151 | thunderstorm,
152 | windy,
153 | }
154 |
155 | /// Temperature unit of measurement.
156 | enum TemperatureUnit {
157 | celsius,
158 | fahrenheit,
159 | }
160 |
161 | /// Removes the enum type and returns the value as a String.
162 | String enumToString(Object e) => e.toString().split('.').last;
163 |
--------------------------------------------------------------------------------
/flutter_clock_helper/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_clock_helper
2 | description: Helper classes for Flutter Clock contest.
3 |
4 | version: 1.0.0+1
5 |
6 | environment:
7 | sdk: ">=2.2.2 <3.0.0"
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 | intl: "^0.16.0"
13 |
14 | cupertino_icons: ^0.1.2
15 |
16 | dev_dependencies:
17 | flutter_test:
18 | sdk: flutter
19 |
20 | flutter:
21 | uses-material-design: true
22 |
--------------------------------------------------------------------------------
/iso_clock/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .packages
28 | .pub-cache/
29 | .pub/
30 | /build/
31 |
32 | # Android related
33 | **/android/**/gradle-wrapper.jar
34 | **/android/.gradle
35 | **/android/captures/
36 | **/android/gradlew
37 | **/android/gradlew.bat
38 | **/android/local.properties
39 | **/android/**/GeneratedPluginRegistrant.java
40 |
41 | # iOS/XCode related
42 | **/ios/**/*.mode1v3
43 | **/ios/**/*.mode2v3
44 | **/ios/**/*.moved-aside
45 | **/ios/**/*.pbxuser
46 | **/ios/**/*.perspectivev3
47 | **/ios/**/*sync/
48 | **/ios/**/.sconsign.dblite
49 | **/ios/**/.tags*
50 | **/ios/**/.vagrant/
51 | **/ios/**/DerivedData/
52 | **/ios/**/Icon?
53 | **/ios/**/Pods/
54 | **/ios/**/.symlinks/
55 | **/ios/**/profile
56 | **/ios/**/xcuserdata
57 | **/ios/.generated/
58 | **/ios/Flutter/App.framework
59 | **/ios/Flutter/Flutter.framework
60 | **/ios/Flutter/Generated.xcconfig
61 | **/ios/Flutter/app.flx
62 | **/ios/Flutter/app.zip
63 | **/ios/Flutter/flutter_assets/
64 | **/ios/Flutter/flutter_export_environment.sh
65 | **/ios/ServiceDefinitions.json
66 | **/ios/Runner/GeneratedPluginRegistrant.*
67 |
68 | # Exceptions to above rules.
69 | !**/ios/**/default.mode1v3
70 | !**/ios/**/default.mode2v3
71 | !**/ios/**/default.pbxuser
72 | !**/ios/**/default.perspectivev3
73 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
74 |
--------------------------------------------------------------------------------
/iso_clock/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 68587a0916366e9512a78df22c44163d041dd5f3
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/iso_clock/README.md:
--------------------------------------------------------------------------------
1 | # iso_clock
2 |
3 | A new Flutter project.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/iso_clock/lib/digit_data.dart:
--------------------------------------------------------------------------------
1 | class DeviceDimensions {
2 | final double parentWidth = 70.0;
3 | final double parentHeight = 110.0;
4 | }
5 |
6 | class RelativeBox {
7 | double left;
8 | double top;
9 | double width;
10 | double height;
11 |
12 | RelativeBox({this.left, this.top, this.width, this.height});
13 | }
14 |
15 |
16 | class DigitData with DeviceDimensions {
17 | bool hasTwo;
18 | RelativeBox box1;
19 | RelativeBox box2;
20 |
21 | DigitData({this.hasTwo, this.box1, this.box2});
22 |
23 | List get box1LTWH {
24 | List absoluteLTWH = new List(4);
25 | absoluteLTWH[0] = box1.left * parentWidth;
26 | absoluteLTWH[1] = box1.top * parentHeight;
27 | absoluteLTWH[2] = box1.width * parentWidth;
28 | absoluteLTWH[3] = box1.height * parentHeight;
29 | return(absoluteLTWH);
30 | }
31 |
32 | List get box2LTWH {
33 | List absoluteLTWH = new List(4);
34 | absoluteLTWH[0] = box2.left * parentWidth;
35 | absoluteLTWH[1] = box2.top * parentHeight;
36 | absoluteLTWH[2] = box2.width * parentWidth;
37 | absoluteLTWH[3] = box2.height * parentHeight;
38 | return(absoluteLTWH);
39 | }
40 | }
41 |
42 | class ListOfDigitData {
43 | List _digits = [
44 | // 0
45 | DigitData(
46 | hasTwo: false,
47 | box1: RelativeBox(left: 0.3, top: 0.2, width: 0.4, height: 0.6),
48 | ),
49 | // 1
50 | DigitData(
51 | hasTwo: true,
52 | box1: RelativeBox(left: 0.0, top: 0.0, width: 0.35, height: 1.0),
53 | box2: RelativeBox(left: 0.65, top: 0.0, width: 0.35, height: 1.0),
54 | ),
55 | // 2
56 | DigitData(
57 | hasTwo: true,
58 | box1: RelativeBox(left: 0.0, top: 0.2, width: 0.7, height: 0.2),
59 | box2: RelativeBox(left: 0.3, top: 0.6, width: 0.7, height: 0.2),
60 | ),
61 | // 3
62 | DigitData(
63 | hasTwo: true,
64 | box1: RelativeBox(left: 0.0, top: 0.2, width: 0.7, height: 0.2),
65 | box2: RelativeBox(left: 0.0, top: 0.6, width: 0.7, height: 0.2),
66 | ),
67 | // 4
68 | DigitData(
69 | hasTwo: true,
70 | box1: RelativeBox(left: 0.3, top: 0.0, width: 0.4, height: 0.4),
71 | box2: RelativeBox(left: 0.0, top: 0.6, width: 0.7, height: 0.4),
72 | ),
73 | // 5
74 | DigitData(
75 | hasTwo: true,
76 | box1: RelativeBox(left: 0.3, top: 0.2, width: 0.7, height: 0.2),
77 | box2: RelativeBox(left: 0.0, top: 0.6, width: 0.7, height: 0.2),
78 | ),
79 | // 6
80 | DigitData(
81 | hasTwo: true,
82 | box1: RelativeBox(left: 0.3, top: 0.2, width: 0.7, height: 0.2),
83 | box2: RelativeBox(left: 0.3, top: 0.6, width: 0.4, height: 0.2),
84 | ),
85 | // 7
86 | DigitData(
87 | hasTwo: false,
88 | box1: RelativeBox(left: 0.0, top: 0.2, width: 0.7, height: 0.8),
89 | ),
90 | // 8
91 | DigitData(
92 | hasTwo: true,
93 | box1: RelativeBox(left: 0.3, top: 0.2, width: 0.4, height: 0.2),
94 | box2: RelativeBox(left: 0.3, top: 0.6, width: 0.4, height: 0.2),
95 | ),
96 | // 9
97 | DigitData(
98 | hasTwo: true,
99 | box1: RelativeBox(left: 0.3, top: 0.2, width: 0.4, height: 0.2),
100 | box2: RelativeBox(left: 0.0, top: 0.6, width: 0.7, height: 0.2),
101 | ),
102 |
103 | ];
104 |
105 | get digits => _digits;
106 | }
--------------------------------------------------------------------------------
/iso_clock/lib/iso_clock.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_clock_helper/model.dart';
4 | import 'package:intl/intl.dart';
5 | import 'package:iso_clock/digit_data.dart';
6 |
7 | enum _Element {
8 | background,
9 | gradientLighter,
10 | gradientDarker,
11 | }
12 |
13 | final _lightTheme = {
14 | _Element.background: Colors.white,
15 | _Element.gradientLighter: [Colors.lightBlue[600], Colors.cyan[100]],
16 | _Element.gradientDarker: [Colors.lightBlue[300], Colors.cyan[100]],
17 | };
18 |
19 | final _darkTheme = {
20 | _Element.background: Colors.black,
21 | _Element.gradientLighter: [Colors.grey[500], Colors.grey[900]],
22 | _Element.gradientDarker: [Colors.grey[700], Colors.grey[900]],
23 | };
24 |
25 | final double _digitWidth = 70.0;
26 | final double _digitHeight = 110.0;
27 |
28 | var _currentTheme = _lightTheme;
29 |
30 | class IsoClock extends StatefulWidget {
31 | const IsoClock(this.model);
32 |
33 | final ClockModel model;
34 |
35 | @override
36 | _IsoClockState createState() => _IsoClockState();
37 | }
38 |
39 | class _IsoClockState extends State {
40 | var _dateTime = DateTime.now();
41 | Timer _timer;
42 |
43 | @override
44 | void initState() {
45 | super.initState();
46 | widget.model.addListener(_updateModel);
47 | _updateTime();
48 | _updateModel();
49 | }
50 |
51 | @override
52 | void didUpdateWidget(IsoClock oldWidget) {
53 | super.didUpdateWidget(oldWidget);
54 | if (widget.model != oldWidget.model) {
55 | oldWidget.model.removeListener(_updateModel);
56 | widget.model.addListener(_updateModel);
57 | }
58 | }
59 |
60 | @override
61 | void dispose() {
62 | _timer?.cancel();
63 | widget.model.removeListener(_updateModel);
64 | widget.model.dispose();
65 | super.dispose();
66 | }
67 |
68 | void _updateModel() {
69 | setState(() {
70 | // Cause the clock to rebuild when the model changes.
71 | });
72 | }
73 |
74 | void _updateTime() {
75 | setState(() {
76 | _dateTime = DateTime.now();
77 | _timer = Timer(
78 | Duration(minutes: 1) -
79 | Duration(seconds: _dateTime.second) -
80 | Duration(milliseconds: _dateTime.millisecond),
81 | _updateTime,
82 | );
83 | });
84 | }
85 |
86 | _firstDigit(int n) {
87 | if (n <= 9) {
88 | return 0;
89 | } else {
90 | return int.parse(n.toString().substring(0, 1));
91 | }
92 | }
93 |
94 | _secondDigit(int n) {
95 | if (n <= 9) {
96 | return n;
97 | } else {
98 | return int.parse(n.toString().substring(1, 2));
99 | }
100 | }
101 |
102 | @override
103 | Widget build(BuildContext context) {
104 | // set theme so it can be accessed by all children widgets
105 | _currentTheme = Theme.of(context).brightness == Brightness.light
106 | ? _lightTheme
107 | : _darkTheme;
108 | final hour = int.parse(DateFormat(widget.model.is24HourFormat ? 'HH' : 'hh')
109 | .format(_dateTime));
110 | final minute = int.parse(DateFormat('mm').format(_dateTime));
111 |
112 | return new Scaffold(
113 | backgroundColor: _currentTheme[_Element.background],
114 | body: Center(
115 | child: Row(
116 | mainAxisAlignment: MainAxisAlignment.spaceAround,
117 | children: [
118 | SizedBox(width: 10),
119 | Digit(time: _firstDigit(hour), delay: 360),
120 | Digit(time: _secondDigit(hour), delay: 240),
121 | Colon(),
122 | Digit(time: _firstDigit(minute), delay: 120),
123 | Digit(time: _secondDigit(minute), delay: 0),
124 | SizedBox(width: 10),
125 | ],
126 | ),
127 | ),
128 | );
129 | }
130 | }
131 |
132 | /// Digit
133 | /// ├── DigitFacade
134 | /// | ├── ThreeDimensionalHole
135 | /// | └── ThreeDimensionalBox
136 | /// └── AntiAliasPerimeterPadding
137 |
138 | class Digit extends StatelessWidget {
139 | final int time;
140 | final int delay;
141 |
142 | Digit({Key key, this.time, this.delay}) : super(key: key);
143 |
144 | @override
145 | Widget build(BuildContext context) {
146 | return Center(
147 | child: Transform(
148 | alignment: FractionalOffset.center,
149 | transform: Matrix4.skewX(-0.2),
150 | child: Container(
151 | width: _digitWidth,
152 | height: _digitHeight,
153 | color: _currentTheme[_Element.background],
154 | child: Stack(
155 | children: [
156 | DigitFacade(time: time, delay: delay),
157 | AntiAliasPerimeterPadding(),
158 | ],
159 | ),
160 | ),
161 | ),
162 | );
163 | }
164 | }
165 |
166 | /// DigitFacade contains the Digit's backdrop, ThreeDimensionalHole,
167 | /// and animations for ThreeDimensionalBox.
168 | /// See README for explanation of anti-aliasing work around
169 | class DigitFacade extends StatefulWidget {
170 | final int time;
171 | final int delay;
172 |
173 | DigitFacade({Key key, this.time, this.delay}) : super(key: key);
174 | @override
175 | _DigitFacadeState createState() => _DigitFacadeState();
176 | }
177 |
178 | class _DigitFacadeState extends State
179 | with SingleTickerProviderStateMixin {
180 | AnimationController _controller;
181 |
182 | int _currentTime;
183 | bool _box1First = true;
184 |
185 | final Interval _quickerCurve = Interval(0.05, 1, curve: Curves.easeInOutQuad);
186 | final Interval _slowerCurve = Interval(0.15, 1, curve: Curves.easeInOutQuad);
187 |
188 | final List _digits = ListOfDigitData().digits;
189 |
190 | // Box One
191 | _animationStateBox1(time) {
192 | return new RelativeRectTween(
193 | begin: new RelativeRect.fromLTRB(
194 | _digits[time].box1LTWH[0], _digits[time].box1LTWH[1], 0.0, 0.0),
195 | end: new RelativeRect.fromLTRB(_digits[time].box1LTWH[0] + 120.0,
196 | _digits[time].box1LTWH[1] + 120.0, 0.0, 0.0),
197 | ).animate(CurvedAnimation(
198 | parent: _controller,
199 | curve: _box1First ? _quickerCurve : _slowerCurve,
200 | ));
201 | }
202 |
203 | // Box Two
204 | _animationStateBox2(time) {
205 | return new RelativeRectTween(
206 | begin: new RelativeRect.fromLTRB(
207 | _digits[time].box2LTWH[0], _digits[time].box2LTWH[1], 0.0, 0.0),
208 | end: new RelativeRect.fromLTRB(_digits[time].box2LTWH[0] + 120.0,
209 | _digits[time].box2LTWH[1] + 120.0, 0.0, 0.0),
210 | ).animate(CurvedAnimation(
211 | parent: _controller,
212 | curve: _box1First ? _slowerCurve : _quickerCurve,
213 | ));
214 | }
215 |
216 | @override
217 | void initState() {
218 | super.initState();
219 | _controller = AnimationController(
220 | duration: Duration(seconds: 1),
221 | vsync: this,
222 | );
223 |
224 | _currentTime = widget.time;
225 | }
226 |
227 | @override
228 | void didUpdateWidget(DigitFacade oldWidget) {
229 | super.didUpdateWidget(oldWidget);
230 | if (widget.time != oldWidget.time) {
231 | // Recess the Boxes currently in view
232 | Future.delayed(Duration(milliseconds: widget.delay), () {
233 | setState(() {
234 | _controller.forward();
235 | _currentTime == 1 ? _box1First = false : _box1First = true;
236 | });
237 | });
238 | // Bring new Boxes into view
239 | Future.delayed(Duration(milliseconds: 600 + widget.delay), () {
240 | setState(() {
241 | _currentTime = widget.time;
242 | _controller.reverse();
243 | _currentTime == 1 ? _box1First = true : _box1First = false;
244 | });
245 | });
246 | }
247 | }
248 |
249 | @override
250 | Widget build(BuildContext context) {
251 | return Center(
252 | child: Container(
253 | width: _digitWidth - 1,
254 | height: _digitHeight -1,
255 | child: ClipRect(
256 | clipBehavior: Clip.antiAlias,
257 | child: Stack(
258 | children: [
259 | ThreeDimensionalHole(),
260 | PositionedTransition(
261 | rect: _animationStateBox1(_currentTime),
262 | child: ThreeDimensionalBox(ltwh: _digits[_currentTime].box1LTWH),
263 | ),
264 | if (_digits[_currentTime].hasTwo)
265 | PositionedTransition(
266 | rect: _animationStateBox2(_currentTime),
267 | child: ThreeDimensionalBox(ltwh: _digits[_currentTime].box2LTWH),
268 | ),
269 | ],
270 | ),
271 | ),
272 | ),
273 | );
274 | }
275 | }
276 |
277 | class ThreeDimensionalHole extends StatelessWidget {
278 | @override
279 | Widget build(BuildContext context) {
280 | return Container(
281 | decoration: BoxDecoration(
282 | gradient: LinearGradient(
283 | begin: Alignment.centerLeft,
284 | end: Alignment.centerRight,
285 | colors: _currentTheme[_Element.gradientDarker],
286 | ),
287 | ),
288 | child: ClipRect(
289 | clipBehavior: Clip.antiAlias,
290 | child: Transform(
291 | alignment: FractionalOffset.topLeft,
292 | transform: Matrix4.skewX(0.7),
293 | child: Container(
294 | decoration: BoxDecoration(
295 | gradient: LinearGradient(
296 | begin: Alignment.topCenter,
297 | end: Alignment.bottomCenter,
298 | colors: _currentTheme[_Element.gradientLighter],
299 | ),
300 | ),
301 | ),
302 | ),
303 | ),
304 | );
305 | }
306 | }
307 |
308 | class ThreeDimensionalBox extends StatelessWidget {
309 | /// ltwh: left, top, width, height of Box in relation
310 | /// to top left corner of parent container DigitFacade
311 | final List ltwh;
312 | ThreeDimensionalBox({this.ltwh});
313 |
314 | @override
315 | Widget build(BuildContext context) {
316 | return Stack(
317 | children: [
318 | // Main Face
319 | Container(
320 | width: ltwh[2],
321 | height: ltwh[3],
322 | color: _currentTheme[_Element.background],
323 | ),
324 |
325 | // Right Face
326 | Padding(
327 | padding: EdgeInsets.only(left: ltwh[2]),
328 | child: Transform(
329 | alignment: FractionalOffset.topLeft,
330 | transform: Matrix4.skewY(0.9),
331 | child: Container(
332 | width: 80.0, // grows down
333 | height: ltwh[3],
334 | decoration: BoxDecoration(
335 | gradient: LinearGradient(
336 | begin: Alignment.centerLeft,
337 | end: Alignment.centerRight,
338 | colors: _currentTheme[_Element.gradientDarker],
339 | ),
340 | ),
341 | ),
342 | ),
343 | ),
344 |
345 | // Bottom Face
346 | Padding(
347 | padding: EdgeInsets.only(top: ltwh[3]),
348 | child: Transform(
349 | alignment: FractionalOffset.topLeft,
350 | transform: Matrix4.skewX(0.7), // skew will not go out of y bounds
351 | child: Container(
352 | width: ltwh[2],
353 | height: 80.0,
354 | decoration: BoxDecoration(
355 | gradient: LinearGradient(
356 | begin: Alignment.topCenter,
357 | end: Alignment.bottomCenter,
358 | colors: _currentTheme[_Element.gradientLighter],
359 | ),
360 | ),
361 | ),
362 | ),
363 | ),
364 | ],
365 | );
366 | }
367 | }
368 |
369 | /// Perimeter padding is an inset "border" used to cover the scaled
370 | /// down DigitFacade's edges that contain anti-aliasing artifacts
371 | class AntiAliasPerimeterPadding extends StatelessWidget {
372 | Widget build(BuildContext context) {
373 | return Stack(
374 | children: [
375 | Row(
376 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
377 | crossAxisAlignment: CrossAxisAlignment.stretch,
378 | children: [
379 | _aliasBumperLR(),
380 | _aliasBumperLR(),
381 | ],
382 | ),
383 | Column(
384 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
385 | crossAxisAlignment: CrossAxisAlignment.stretch,
386 | children: [
387 | _aliasBumperTB(),
388 | _aliasBumperTB(),
389 | ],
390 | ),
391 | ],
392 | );
393 | }
394 |
395 | Widget _aliasBumperLR() {
396 | return Container(
397 | width: 1,
398 | color: _currentTheme[_Element.background],
399 | );
400 | }
401 |
402 | Widget _aliasBumperTB() {
403 | return Container(
404 | height: 1,
405 | color: _currentTheme[_Element.background],
406 | );
407 | }
408 | }
409 |
410 | class Colon extends StatelessWidget {
411 | @override
412 | Widget build(BuildContext context) {
413 | return Transform(
414 | alignment: FractionalOffset.center,
415 | transform: Matrix4.skewX(-0.2),
416 | child: Container(
417 | height: 90,
418 | child: Column(
419 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
420 | children: [
421 | Container(
422 | height: 15,
423 | width: 15,
424 | child: ThreeDimensionalHole(),
425 | ),
426 | Container(
427 | height: 15,
428 | width: 15,
429 | child: ThreeDimensionalHole(),
430 | ),
431 | ],
432 | ),
433 | ),
434 | );
435 | }
436 | }
437 |
--------------------------------------------------------------------------------
/iso_clock/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter_clock_helper/customizer.dart';
4 | import 'package:flutter_clock_helper/model.dart';
5 | import 'package:flutter/foundation.dart';
6 | import 'package:flutter/material.dart';
7 |
8 | import 'iso_clock.dart';
9 |
10 | void main() {
11 | // A temporary measure until Platform supports web and TargetPlatform supports
12 | // macOS.
13 | if (!kIsWeb && Platform.isMacOS) {
14 | // https://github.com/flutter/flutter/issues/31366
15 | // See https://github.com/flutter/flutter/wiki/Desktop-shells#target-platform-override.
16 | debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
17 | }
18 | runApp(ClockCustomizer((ClockModel model) => IsoClock(model)));
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/iso_clock/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | archive:
5 | dependency: transitive
6 | description:
7 | name: archive
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.0.11"
11 | args:
12 | dependency: transitive
13 | description:
14 | name: args
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "1.5.2"
18 | async:
19 | dependency: transitive
20 | description:
21 | name: async
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "2.4.0"
25 | boolean_selector:
26 | dependency: transitive
27 | description:
28 | name: boolean_selector
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.0.5"
32 | charcode:
33 | dependency: transitive
34 | description:
35 | name: charcode
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.1.2"
39 | collection:
40 | dependency: transitive
41 | description:
42 | name: collection
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.14.11"
46 | convert:
47 | dependency: transitive
48 | description:
49 | name: convert
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "2.1.1"
53 | crypto:
54 | dependency: transitive
55 | description:
56 | name: crypto
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "2.1.3"
60 | cupertino_icons:
61 | dependency: "direct main"
62 | description:
63 | name: cupertino_icons
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "0.1.3"
67 | flutter:
68 | dependency: "direct main"
69 | description: flutter
70 | source: sdk
71 | version: "0.0.0"
72 | flutter_clock_helper:
73 | dependency: "direct main"
74 | description:
75 | path: "..\\flutter_clock_helper"
76 | relative: true
77 | source: path
78 | version: "1.0.0+1"
79 | flutter_test:
80 | dependency: "direct dev"
81 | description: flutter
82 | source: sdk
83 | version: "0.0.0"
84 | image:
85 | dependency: transitive
86 | description:
87 | name: image
88 | url: "https://pub.dartlang.org"
89 | source: hosted
90 | version: "2.1.4"
91 | intl:
92 | dependency: transitive
93 | description:
94 | name: intl
95 | url: "https://pub.dartlang.org"
96 | source: hosted
97 | version: "0.16.0"
98 | matcher:
99 | dependency: transitive
100 | description:
101 | name: matcher
102 | url: "https://pub.dartlang.org"
103 | source: hosted
104 | version: "0.12.6"
105 | meta:
106 | dependency: transitive
107 | description:
108 | name: meta
109 | url: "https://pub.dartlang.org"
110 | source: hosted
111 | version: "1.1.8"
112 | path:
113 | dependency: transitive
114 | description:
115 | name: path
116 | url: "https://pub.dartlang.org"
117 | source: hosted
118 | version: "1.6.4"
119 | pedantic:
120 | dependency: transitive
121 | description:
122 | name: pedantic
123 | url: "https://pub.dartlang.org"
124 | source: hosted
125 | version: "1.8.0+1"
126 | petitparser:
127 | dependency: transitive
128 | description:
129 | name: petitparser
130 | url: "https://pub.dartlang.org"
131 | source: hosted
132 | version: "2.4.0"
133 | quiver:
134 | dependency: transitive
135 | description:
136 | name: quiver
137 | url: "https://pub.dartlang.org"
138 | source: hosted
139 | version: "2.0.5"
140 | sky_engine:
141 | dependency: transitive
142 | description: flutter
143 | source: sdk
144 | version: "0.0.99"
145 | source_span:
146 | dependency: transitive
147 | description:
148 | name: source_span
149 | url: "https://pub.dartlang.org"
150 | source: hosted
151 | version: "1.5.5"
152 | stack_trace:
153 | dependency: transitive
154 | description:
155 | name: stack_trace
156 | url: "https://pub.dartlang.org"
157 | source: hosted
158 | version: "1.9.3"
159 | stream_channel:
160 | dependency: transitive
161 | description:
162 | name: stream_channel
163 | url: "https://pub.dartlang.org"
164 | source: hosted
165 | version: "2.0.0"
166 | string_scanner:
167 | dependency: transitive
168 | description:
169 | name: string_scanner
170 | url: "https://pub.dartlang.org"
171 | source: hosted
172 | version: "1.0.5"
173 | term_glyph:
174 | dependency: transitive
175 | description:
176 | name: term_glyph
177 | url: "https://pub.dartlang.org"
178 | source: hosted
179 | version: "1.1.0"
180 | test_api:
181 | dependency: transitive
182 | description:
183 | name: test_api
184 | url: "https://pub.dartlang.org"
185 | source: hosted
186 | version: "0.2.11"
187 | typed_data:
188 | dependency: transitive
189 | description:
190 | name: typed_data
191 | url: "https://pub.dartlang.org"
192 | source: hosted
193 | version: "1.1.6"
194 | vector_math:
195 | dependency: transitive
196 | description:
197 | name: vector_math
198 | url: "https://pub.dartlang.org"
199 | source: hosted
200 | version: "2.0.8"
201 | xml:
202 | dependency: transitive
203 | description:
204 | name: xml
205 | url: "https://pub.dartlang.org"
206 | source: hosted
207 | version: "3.5.0"
208 | sdks:
209 | dart: ">=2.4.0 <3.0.0"
210 |
--------------------------------------------------------------------------------
/iso_clock/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: iso_clock
2 | description: Isometric Representation of Clock
3 |
4 | # The following defines the version and build number for your application.
5 | # A version number is three numbers separated by dots, like 1.2.43
6 | # followed by an optional build number separated by a +.
7 | # Both the version and the builder number may be overridden in flutter
8 | # build by specifying --build-name and --build-number, respectively.
9 | # In Android, build-name is used as versionName while build-number used as versionCode.
10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
12 | # Read more about iOS versioning at
13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
14 | version: 1.0.0+1
15 |
16 | environment:
17 | sdk: ">=2.1.0 <3.0.0"
18 |
19 | dependencies:
20 | flutter:
21 | sdk: flutter
22 | flutter_clock_helper:
23 | path: ../flutter_clock_helper
24 | cupertino_icons: ^0.1.2
25 |
26 | dev_dependencies:
27 | flutter_test:
28 | sdk: flutter
29 |
30 |
31 | # For information on the generic Dart part of this file, see the
32 | # following page: https://dart.dev/tools/pub/pubspec
33 |
34 | # The following section is specific to Flutter.
35 | flutter:
36 |
37 | # The following line ensures that the Material Icons font is
38 | # included with your application, so that you can use the icons in
39 | # the material Icons class.
40 | uses-material-design: true
41 |
42 | # To add assets to your application, add an assets section, like this:
43 | # assets:
44 | # - images/a_dot_burr.jpeg
45 | # - images/a_dot_ham.jpeg
46 |
47 | # An image asset can refer to one or more resolution-specific "variants", see
48 | # https://flutter.dev/assets-and-images/#resolution-aware.
49 |
50 | # For details regarding adding assets from package dependencies, see
51 | # https://flutter.dev/assets-and-images/#from-packages
52 |
53 | # To add custom fonts to your application, add a fonts section here,
54 | # in this "flutter" section. Each entry in this list should have a
55 | # "family" key with the font family name, and a "fonts" key with a
56 | # list giving the asset and other descriptors for the font. For
57 | # example:
58 | # fonts:
59 | # - family: Schyler
60 | # fonts:
61 | # - asset: fonts/Schyler-Regular.ttf
62 | # - asset: fonts/Schyler-Italic.ttf
63 | # style: italic
64 | # - family: Trajan Pro
65 | # fonts:
66 | # - asset: fonts/TrajanPro.ttf
67 | # - asset: fonts/TrajanPro_Bold.ttf
68 | # weight: 700
69 | #
70 | # For details regarding fonts from package dependencies,
71 | # see https://flutter.dev/custom-fonts/#from-packages
72 |
--------------------------------------------------------------------------------
/iso_clock_dark_theme.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cookmscott/FlutterClockChallenge/03cd17593f7734517582b0c5b78e919e2cd0b541/iso_clock_dark_theme.gif
--------------------------------------------------------------------------------
/iso_clock_light_theme.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cookmscott/FlutterClockChallenge/03cd17593f7734517582b0c5b78e919e2cd0b541/iso_clock_light_theme.gif
--------------------------------------------------------------------------------
/iso_clock_scott_cook.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cookmscott/FlutterClockChallenge/03cd17593f7734517582b0c5b78e919e2cd0b541/iso_clock_scott_cook.zip
--------------------------------------------------------------------------------