Commit 1ed68f5f authored by Nicolas Richard Walter Boeckh's avatar Nicolas Richard Walter Boeckh 💬
Browse files

UX/UI, new pref prefab

- Added Dashed line for UX
- Beautify Settings form
- Prefs to const system
parent 10a07f78
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"android":[{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"macos":[{"name":"shared_preferences_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_macos-0.0.1+8\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"shared_preferences_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_web-0.1.2+5\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"flutter_blue","dependencies":[]},{"name":"geolocator","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","dependencies":[]},{"name":"location_permissions","dependencies":[]},{"name":"path_provider","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_macos","shared_preferences_web"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2020-09-01 02:30:16.307969","version":"1.21.0-10.0.pre.193"}
\ No newline at end of file
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"android":[{"name":"flutter_blue","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\flutter_blue-0.7.2\\\\","dependencies":[]},{"name":"geolocator","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\geolocator-5.2.1\\\\","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\google_api_availability-2.0.2\\\\","dependencies":[]},{"name":"location_permissions","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\location_permissions-2.0.4+1\\\\","dependencies":[]},{"name":"path_provider","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\path_provider-1.6.0\\\\","dependencies":[]},{"name":"permission_handler","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\permission_handler-4.2.0+hotfix.3\\\\","dependencies":[]},{"name":"shared_preferences","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences-0.5.7+2\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"macos":[{"name":"shared_preferences_macos","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_macos-0.0.1+8\\\\","dependencies":[]},{"name":"sqflite","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\sqflite-1.2.0\\\\","dependencies":[]}],"linux":[],"windows":[],"web":[{"name":"shared_preferences_web","path":"C:\\\\src\\\\flutter\\\\.pub-cache\\\\hosted\\\\pub.dartlang.org\\\\shared_preferences_web-0.1.2+5\\\\","dependencies":[]}]},"dependencyGraph":[{"name":"flutter_blue","dependencies":[]},{"name":"geolocator","dependencies":["google_api_availability","location_permissions"]},{"name":"google_api_availability","dependencies":[]},{"name":"location_permissions","dependencies":[]},{"name":"path_provider","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_macos","shared_preferences_web"]},{"name":"shared_preferences_macos","dependencies":[]},{"name":"shared_preferences_web","dependencies":[]},{"name":"sqflite","dependencies":[]}],"date_created":"2020-09-02 01:23:48.043026","version":"1.21.0-10.0.pre.193"}
\ No newline at end of file
import 'package:logair_application/utils/enums/preference_keys.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tuple/tuple.dart';
/// Handler enabling the storage and use of preferences.
class PreferencesHandler {
Map<String, Tuple2<dynamic, dynamic>> _defaultValues = {
'BT.USING_ADVANCED': Tuple2(bool, false),
'BT.FREQUENT': Tuple2(String, '[ ]'),
'BT.SERVICE_UUID': Tuple2(String, '0000ffe0-0000-1000-8000-00805f9b34fb'),
'BT.CHARACTERISTIC_UUID': Tuple2(String, '0000ffe1-0000-1000-8000-00805f9b34fb'),
'MAP.USING_ADVANCED': Tuple2(bool, false),
'MAP.PROX.MAX_PER_QUERY': Tuple2(int, 200),
'MAP.PROX.MAX_AGE_SECONDS': Tuple2(int, 200)
};
static final PreferencesHandler _singleton = new PreferencesHandler._internal();
SharedPreferences _sharedPreferencesInternal;
bool _setup = false;
Future<SharedPreferences> get _sharedPreferences async {
if (_sharedPreferencesInternal == null) {
// If the assertion fails
if (!PreferenceKeys.runAssertion()) {
throw new Exception('A <defaultValue<T>, T> pair is mismatched');
}
if (_sharedPreferencesInternal == null && !this._setup) {
this._sharedPreferencesInternal = await SharedPreferences.getInstance();
/// Initial Setup
_defaultValues.forEach((key, value) {
PreferenceKeys.list.forEach((PreferenceKeys prefs) {
Function setter;
switch (value.item1) {
switch (prefs.type) {
case int:
setter = this._sharedPreferencesInternal.setInt;
break;
......@@ -36,12 +33,13 @@ class PreferencesHandler {
default:
throw ArgumentError('Unexpected property type');
}
if (!this._sharedPreferencesInternal.containsKey(key)) {
setter(key, value.item2);
setter(key + '_DEFAULT', value.item2);
if (!this._sharedPreferencesInternal.containsKey(prefs.key)) {
setter(prefs.key, prefs.defaultValue);
setter(prefs.defaultKey, prefs.defaultValue);
}
});
print('Setup preferences \n ${List.generate(this._sharedPreferencesInternal.getKeys().length, (i) => this._sharedPreferencesInternal.getKeys().elementAt(i) + ' => ' + this._sharedPreferencesInternal.get(this._sharedPreferencesInternal.getKeys().elementAt(i)).toString()).join("\n")}');
});
print('Setup preferences done\n ${List.generate(this._sharedPreferencesInternal.getKeys().length, (i) => this._sharedPreferencesInternal.getKeys().elementAt(i) + ' => ' + this._sharedPreferencesInternal.get(this._sharedPreferencesInternal.getKeys().elementAt(i)).toString()).join("\n")}');
this._setup = true;
}
return this._sharedPreferencesInternal;
......@@ -59,24 +57,21 @@ class PreferencesHandler {
Future<void> setPreferencesString(String key, String value) async => (await this._sharedPreferences).setString(key, value);
Future<void> setPreferencesBool(String key, bool value) async => (await this._sharedPreferences).setBool(key, value);
Future<void> resetPreferences<T>(String key) async {
Function getter, setter;
switch (T) {
Future<void> resetPreferences(String key) async {
Function setter;
PreferenceKeys pref = PreferenceKeys.get(key);
switch (pref.type) {
case int:
getter = getPreferencesInt;
setter = setPreferencesInt;
break;
case bool:
getter = getPreferencesBool;
setter = setPreferencesBool;
break;
case String:
getter = getPreferencesString;
setter = setPreferencesString;
}
T value = await getter(key + '_DEFAULT');
await setter(key, value);
await setter(pref.key, pref.defaultValue);
}
void resetAllPreferences() async {
......
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:intersperse/intersperse.dart';
import 'package:logair_application/logic/handlers/data_handler.dart';
import 'package:logair_application/ui/components/common/carousel_card.dart';
import 'package:logair_application/ui/components/body/data/graph/graph.dart';
import 'package:logair_application/ui/components/body/data/tiles/data_tile.dart';
import 'package:logair_application/ui/components/common/dash_separator.dart';
class DataWidget extends StatelessWidget {
DataWidget();
......@@ -17,13 +19,15 @@ class DataWidget extends StatelessWidget {
children: [
// Local data (Chart + Data points)
Graph(),
DataTile(DataHandler().getDataStream(), false, ''),
DataTile(stream: DataHandler().getDataStream(), isDistant: false),
SizedBox(height: 10,),
// Bar (Locations + Add new -> Pop to new route)
Container(
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.only(topLeft: Radius.circular(5), topRight: Radius.circular(5))
borderRadius: BorderRadius.vertical(
top: Radius.circular(5)
)
),
child: Row(
children: [
......@@ -32,7 +36,7 @@ class DataWidget extends StatelessWidget {
'Locations of Interest',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18
fontSize: 22
),
),
),
......@@ -44,16 +48,25 @@ class DataWidget extends StatelessWidget {
),
),
Column(
children: [
DataTile(DataHandler().getMockDataStream(), true, 'Home'),
DataTile(DataHandler().getMockDataStream(), true, 'Work'),
DataTile(DataHandler().getMockDataStream(), true, 'C'),
DataTile(DataHandler().getMockDataStream(), true, 'D'),
DataTile(DataHandler().getMockDataStream(), true, 'E'),
DataTile(DataHandler().getMockDataStream(), true, 'F'),
DataTile(DataHandler().getMockDataStream(), true, 'G'),
DataTile(DataHandler().getMockDataStream(), true, 'H'),
DataTile(DataHandler().getMockDataStream(), true, 'I'),
children: intersperse(
Container(
margin: EdgeInsets.symmetric(vertical: 5),
child: DashSeparator(
color: Colors.grey[400],
),
),
[
DataTile(stream: DataHandler().getMockDataStream(), locationName: 'Home'),
DataTile(stream: DataHandler().getMockDataStream(), locationName: 'Work'),
DataTile(stream: DataHandler().getMockDataStream(), locationName: 'C'),
DataTile(stream: DataHandler().getMockDataStream(), locationName: 'D'),
DataTile(stream: DataHandler().getMockDataStream(), locationName: 'E'),
DataTile(stream: DataHandler().getMockDataStream(), locationName: 'F'),
DataTile(stream: DataHandler().getMockDataStream(), locationName: 'G'),
DataTile(stream: DataHandler().getMockDataStream(), locationName: 'H'),
DataTile(stream: DataHandler().getMockDataStream(), locationName: 'I')
]
).toList() + [
SizedBox(height: 30,)
],
),
......
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:logair_application/logic/data_packet.dart';
import 'package:logair_application/ui/components/common/dash_separator.dart';
import 'package:logair_application/utils/enums/pm_symbol.dart';
//TODO Add weather ?
......@@ -19,7 +20,7 @@ class DataTile extends StatefulWidget {
final String locationName;
DataTile(this.stream, this.isDistant, this.locationName, {Key key}) : super(key: key);
DataTile({@required this.stream, this.isDistant = true, this.locationName = '', Key key}) : super(key: key);
/// Provides [stream] to the [_DataTileState]
@override
......@@ -143,11 +144,6 @@ class _DataTileState extends State<DataTile> {
);
return Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(width: (this.isDistant) ? 1 : 0, color: Theme.of(context).dividerColor)
)
),
margin: EdgeInsets.symmetric(horizontal: 3),
child: Column(
children: [
......@@ -157,7 +153,7 @@ class _DataTileState extends State<DataTile> {
child: Text(
'Location: ${this.locationName}',
style: TextStyle(
fontSize: 16,
fontSize: 18,
fontWeight: FontWeight.bold
)
)
......@@ -210,9 +206,9 @@ class _DataTileState extends State<DataTile> {
),
]
),
)
),
],
)
),
],
),
);
......
// 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.
// @dart = 2.8
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
const Duration _kExpand = Duration(milliseconds: 200);
/// A single-line [ListTile] with a trailing button that expands or collapses
/// the tile to reveal or hide the [children].
///
/// This widget is typically used with [ListView] to create an
/// "expand / collapse" list entry. When used with scrolling widgets like
/// [ListView], a unique [PageStorageKey] must be specified to enable the
/// [ExpansionTile] to save and restore its expanded state when it is scrolled
/// in and out of view.
///
/// See also:
///
/// * [ListTile], useful for creating expansion tile [children] when the
/// expansion tile represents a sublist.
/// * The "Expand/collapse" section of
/// <https://material.io/guidelines/components/lists-controls.html>.
class CustomExpansionTile extends StatefulWidget {
/// Creates a single-line [ListTile] with a trailing button that expands or collapses
/// the tile to reveal or hide the [children]. The [initiallyExpanded] property must
/// be non-null.
const CustomExpansionTile({
Key key,
this.leading,
@required this.title,
this.subtitle,
this.backgroundColor,
this.onExpansionChanged,
this.children = const <Widget>[],
this.trailing,
this.initiallyExpanded = false,
this.maintainState = false,
this.tilePadding,
this.expandedCrossAxisAlignment,
this.expandedAlignment,
this.childrenPadding,
}) : assert(initiallyExpanded != null),
assert(maintainState != null),
assert(
expandedCrossAxisAlignment != CrossAxisAlignment.baseline,
'CrossAxisAlignment.baseline is not supported since the expanded children '
'are aligned in a column, not a row. Try to use another constant.',
),
super(key: key);
/// A widget to display before the title.
///
/// Typically a [CircleAvatar] widget.
final Widget leading;
/// The primary content of the list item.
///
/// Typically a [Text] widget.
final Widget title;
/// Additional content displayed below the title.
///
/// Typically a [Text] widget.
final Widget subtitle;
/// Called when the tile expands or collapses.
///
/// When the tile starts expanding, this function is called with the value
/// true. When the tile starts collapsing, this function is called with
/// the value false.
final ValueChanged<bool> onExpansionChanged;
/// The widgets that are displayed when the tile expands.
///
/// Typically [ListTile] widgets.
final List<Widget> children;
/// The color to display behind the sublist when expanded.
final Color backgroundColor;
/// A widget to display instead of a rotating arrow icon.
final Widget trailing;
/// Specifies if the list tile is initially expanded (true) or collapsed (false, the default).
final bool initiallyExpanded;
/// Specifies whether the state of the children is maintained when the tile expands and collapses.
///
/// When true, the children are kept in the tree while the tile is collapsed.
/// When false (default), the children are removed from the tree when the tile is
/// collapsed and recreated upon expansion.
final bool maintainState;
/// Specifies padding for the [ListTile].
///
/// Analogous to [ListTile.contentPadding], this property defines the insets for
/// the [leading], [title], [subtitle] and [trailing] widgets. It does not inset
/// the expanded [children] widgets.
///
/// When the value is null, the tile's padding is `EdgeInsets.symmetric(horizontal: 16.0)`.
final EdgeInsetsGeometry tilePadding;
/// Specifies the alignment of [children], which are arranged in a column when
/// the tile is expanded.
///
/// The internals of the expanded tile make use of a [Column] widget for
/// [children], and [Align] widget to align the column. The `expandedAlignment`
/// parameter is passed directly into the [Align].
///
/// Modifying this property controls the alignment of the column within the
/// expanded tile, not the alignment of [children] widgets within the column.
/// To align each child within [children], see [expandedCrossAxisAlignment].
///
/// The width of the column is the width of the widest child widget in [children].
///
/// When the value is null, the value of `expandedAlignment` is [Alignment.center].
final Alignment expandedAlignment;
/// Specifies the alignment of each child within [children] when the tile is expanded.
///
/// The internals of the expanded tile make use of a [Column] widget for
/// [children], and the `crossAxisAlignment` parameter is passed directly into the [Column].
///
/// Modifying this property controls the cross axis alignment of each child
/// within its [Column]. Note that the width of the [Column] that houses
/// [children] will be the same as the widest child widget in [children]. It is
/// not necessarily the width of [Column] is equal to the width of expanded tile.
///
/// To align the [Column] along the expanded tile, use the [expandedAlignment] property
/// instead.
///
/// When the value is null, the value of `expandedCrossAxisAlignment` is [CrossAxisAlignment.center].
final CrossAxisAlignment expandedCrossAxisAlignment;
/// Specifies padding for [children].
///
/// When the value is null, the value of `childrenPadding` is [EdgeInsets.zero].
final EdgeInsetsGeometry childrenPadding;
@override
_ExpansionTileState createState() => _ExpansionTileState();
}
class _ExpansionTileState extends State<CustomExpansionTile> with SingleTickerProviderStateMixin {
static final Animatable<double> _easeOutTween = CurveTween(curve: Curves.easeOut);
static final Animatable<double> _easeInTween = CurveTween(curve: Curves.easeIn);
static final Animatable<double> _halfTween = Tween<double>(begin: 0.0, end: 0.5);
final ColorTween _borderColorTween = ColorTween();
final ColorTween _headerColorTween = ColorTween();
final ColorTween _iconColorTween = ColorTween();
final ColorTween _backgroundColorTween = ColorTween();
AnimationController _controller;
Animation<double> _iconTurns;
Animation<double> _heightFactor;
Animation<Color> _headerColor;
Animation<Color> _iconColor;
Animation<Color> _backgroundColor;
bool _isExpanded = false;
@override
void initState() {
super.initState();
_controller = AnimationController(duration: _kExpand, vsync: this);
_heightFactor = _controller.drive(_easeInTween);
_iconTurns = _controller.drive(_halfTween.chain(_easeInTween));
_headerColor = _controller.drive(_headerColorTween.chain(_easeInTween));
_iconColor = _controller.drive(_iconColorTween.chain(_easeInTween));
_backgroundColor = _controller.drive(_backgroundColorTween.chain(_easeOutTween));
_isExpanded = PageStorage.of(context)?.readState(context) as bool ?? widget.initiallyExpanded;
if (_isExpanded)
_controller.value = 1.0;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _handleTap() {
setState(() {
_isExpanded = !_isExpanded;
if (_isExpanded) {
_controller.forward();
} else {
_controller.reverse().then<void>((void value) {
if (!mounted)
return;
setState(() {
// Rebuild without widget.children.
});
});
}
PageStorage.of(context)?.writeState(context, _isExpanded);
});
if (widget.onExpansionChanged != null)
widget.onExpansionChanged(_isExpanded);
}
Widget _buildChildren(BuildContext context, Widget child) {
return Container(
decoration: BoxDecoration(
color: _backgroundColor.value ?? Colors.transparent
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTileTheme.merge(
iconColor: _iconColor.value,
textColor: _headerColor.value,
child: ListTile(
onTap: _handleTap,
contentPadding: widget.tilePadding,
leading: widget.leading,
title: widget.title,
subtitle: widget.subtitle,
trailing: widget.trailing ?? RotationTransition(
turns: _iconTurns,
child: const Icon(Icons.expand_more),
),
),
),
ClipRect(
child: Align(
alignment: widget.expandedAlignment ?? Alignment.center,
heightFactor: _heightFactor.value,
child: child,
),
),
],
),
);
}
@override
void didChangeDependencies() {
final ThemeData theme = Theme.of(context);
_borderColorTween.end = theme.dividerColor;
_headerColorTween
..begin = theme.textTheme.subtitle1.color
..end = theme.accentColor;
_iconColorTween
..begin = theme.unselectedWidgetColor
..end = theme.accentColor;
_backgroundColorTween.end = widget.backgroundColor;
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
final bool closed = !_isExpanded && _controller.isDismissed;
final bool shouldRemoveChildren = closed && !widget.maintainState;
final Widget result = Offstage(
child: TickerMode(
child: Padding(
padding: widget.childrenPadding ?? EdgeInsets.zero,
child: Column(
crossAxisAlignment: widget.expandedCrossAxisAlignment ?? CrossAxisAlignment.center,
children: widget.children,
),
),
enabled: !closed,
),
offstage: closed
);
return AnimatedBuilder(
animation: _controller.view,
builder: _buildChildren,
child: shouldRemoveChildren ? null : result,
);
}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class DashSeparator extends StatelessWidget {
final double height;
final Color color;
const DashSeparator({this.height = 1, this.color = Colors.black});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext ctx, BoxConstraints constraints) {
final double width = constraints.constrainWidth();
final double dashWidth = 10;
final double dashHeight = this.height;
final int dashCount = (width / (2*dashWidth)).floor();
return Flex(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
direction: Axis.horizontal,
children: List.generate(
dashCount,
(_) => SizedBox(
width: dashWidth,
height: dashHeight,
child: DecoratedBox(
decoration: BoxDecoration(
color: color
),
),
)
),
);
},
);
}
}
\ No newline at end of file
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:intersperse/intersperse.dart';
import 'package:logair_application/logic/handlers/database_handler.dart';
import 'package:logair_application/logic/handlers/preference_handler.dart';
import 'package:logair_application/ui/components/common/custom_expansion_tile.dart';
import 'package:logair_application/ui/components/common/dash_separator.dart';
/// This [Widget] is a [Form] that enables users to change settings.
/// It interacts with the [PreferencesHandler] to affect changes that will be maintained on app restart.
......@@ -59,12 +62,18 @@ class _SettingsFormState extends State<SettingsForm> {
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
children: intersperse(
Container(
margin: EdgeInsets.symmetric(vertical: 4),
child: DashSeparator(color: Colors.grey[400]),
),
[
// NETWORKING
ExpansionTile(
CustomExpansionTile(
title: Text('Network'),
initiallyExpanded: true,
leading: Icon(Icons.network_check),
children: [
ListTile(title: Text('TODO: Max number of items per push (def 100)')),
ListTile(title: Text('TODO: Push Frequency')),
......@@ -72,7 +81,7 @@ class _SettingsFormState extends State<SettingsForm> {
],
),
// CACHING
ExpansionTile(
CustomExpansionTile(
title: Text('Local Data'),
initiallyExpanded: true,
leading: Icon(Icons.storage),
......@@ -81,7 +90,7 @@ class _SettingsFormState extends State<SettingsForm> {
],
),
// BLUETOOTH
ExpansionTile(
CustomExpansionTile(
title: Text('Bluetooth'),
subtitle: Text('(Advanced)'),
leading: Icon(Icons.bluetooth),
......@@ -109,12 +118,12 @@ class _SettingsFormState extends State<SettingsForm> {
),
),
// TODO Service / Characteristic Explorer ?
ListTile(title: _buildInputTile<String>(_bleServiceController, 'BLE Service UUID', 'BT.SERVICE_UUID', _advancedBTOptions, TextInputType.text, <TextInputFormatter>[ ], RegExp('[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}'))),
ListTile(title: _buildInputTile<String>(_bleCharacteristicController, 'BLE Characteristic UUID', 'BT.CHARACTERISTIC_UUID', _advancedBTOptions, TextInputType.text, <TextInputFormatter>[ ], RegExp('[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}'))),