settings: svg background
This commit is contained in:
parent
5311caf494
commit
7a8e8503af
14 changed files with 149 additions and 82 deletions
|
@ -1,4 +1,5 @@
|
|||
import 'package:aves/model/settings.dart';
|
||||
import 'package:aves/widgets/common/data_providers/settings_provider.dart';
|
||||
import 'package:aves/widgets/common/icons.dart';
|
||||
import 'package:aves/widgets/home_page.dart';
|
||||
import 'package:aves/widgets/welcome_page.dart';
|
||||
|
@ -37,7 +38,10 @@ class _AvesAppState extends State<AvesApp> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
// place the settings provider above `MaterialApp`
|
||||
// so it can be used during navigation transitions
|
||||
return SettingsProvider(
|
||||
child: MaterialApp(
|
||||
title: 'Aves',
|
||||
theme: ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
|
@ -66,6 +70,7 @@ class _AvesAppState extends State<AvesApp> {
|
|||
return settings.hasAcceptedTerms ? HomePage() : WelcomePage();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,9 @@ final Settings settings = Settings._private();
|
|||
|
||||
typedef SettingsCallback = void Function(String key, dynamic oldValue, dynamic newValue);
|
||||
|
||||
class Settings {
|
||||
class Settings extends ChangeNotifier {
|
||||
static SharedPreferences _prefs;
|
||||
|
||||
final ObserverList<SettingsCallback> _listeners = ObserverList<SettingsCallback>();
|
||||
|
||||
Settings._private();
|
||||
|
||||
// preferences
|
||||
|
@ -28,6 +26,7 @@ class Settings {
|
|||
static const infoMapZoomKey = 'info_map_zoom';
|
||||
static const launchPageKey = 'launch_page';
|
||||
static const coordinateFormatKey = 'coordinates_format';
|
||||
static const svgBackgroundKey = 'svg_background';
|
||||
|
||||
Future<void> init() async {
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
|
@ -37,26 +36,6 @@ class Settings {
|
|||
return _prefs.clear();
|
||||
}
|
||||
|
||||
void addListener(SettingsCallback listener) => _listeners.add(listener);
|
||||
|
||||
void removeListener(SettingsCallback listener) => _listeners.remove(listener);
|
||||
|
||||
void notifyListeners(String key, dynamic oldValue, dynamic newValue) {
|
||||
debugPrint('$runtimeType notifyListeners key=$key, old=$oldValue, new=$newValue');
|
||||
if (_listeners != null) {
|
||||
final localListeners = _listeners.toList();
|
||||
for (final listener in localListeners) {
|
||||
try {
|
||||
if (_listeners.contains(listener)) {
|
||||
listener(key, oldValue, newValue);
|
||||
}
|
||||
} catch (exception, stack) {
|
||||
debugPrint('$runtimeType failed to notify listeners with exception=$exception\n$stack');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String get catalogTimeZone => _prefs.getString(catalogTimeZoneKey) ?? '';
|
||||
|
||||
set catalogTimeZone(String newValue) => setAndNotify(catalogTimeZoneKey, newValue);
|
||||
|
@ -93,6 +72,10 @@ class Settings {
|
|||
|
||||
set coordinateFormat(CoordinateFormat newValue) => setAndNotify(coordinateFormatKey, newValue.toString());
|
||||
|
||||
int get svgBackground => _prefs.getInt(svgBackgroundKey) ?? 0xFFFFFFFF;
|
||||
|
||||
set svgBackground(int newValue) => setAndNotify(svgBackgroundKey, newValue);
|
||||
|
||||
// convenience methods
|
||||
|
||||
// ignore: avoid_positional_boolean_parameters
|
||||
|
@ -133,7 +116,7 @@ class Settings {
|
|||
_prefs.setBool(key, newValue);
|
||||
}
|
||||
if (oldValue != newValue) {
|
||||
notifyListeners(key, oldValue, newValue);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,6 @@ class Constants {
|
|||
],
|
||||
);
|
||||
|
||||
static const svgBackground = Colors.white;
|
||||
static const svgColorFilter = ColorFilter.mode(svgBackground, BlendMode.dstOver);
|
||||
|
||||
static const List<Dependency> androidDependencies = [
|
||||
Dependency(
|
||||
name: 'CWAC-Document',
|
||||
|
|
|
@ -87,7 +87,8 @@ class SectionHeader extends StatelessWidget {
|
|||
// force a higher first line to match leading icon/selector dimension
|
||||
style: TextStyle(height: 2.3 * textScaleFactor),
|
||||
), // 23 hair spaces match a width of 40.0
|
||||
if (hasTrailing) TextSpan(text: '\u200A' * 17),
|
||||
if (hasTrailing)
|
||||
TextSpan(text: '\u200A' * 17),
|
||||
TextSpan(
|
||||
text: text,
|
||||
style: Constants.titleTextStyle,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/utils/constants.dart';
|
||||
import 'package:aves/model/settings.dart';
|
||||
import 'package:aves/widgets/common/image_providers/uri_picture_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ThumbnailVectorImage extends StatelessWidget {
|
||||
final ImageEntry entry;
|
||||
|
@ -23,14 +24,20 @@ class ThumbnailVectorImage extends StatelessWidget {
|
|||
// so that `SvgPicture` doesn't get aligned by the `Stack` like the overlay icons
|
||||
width: extent,
|
||||
height: extent,
|
||||
child: SvgPicture(
|
||||
child: Selector<Settings, int>(
|
||||
selector: (context, s) => s.svgBackground,
|
||||
builder: (context, svgBackground, child) {
|
||||
final colorFilter = ColorFilter.mode(Color(svgBackground), BlendMode.dstOver);
|
||||
return SvgPicture(
|
||||
UriPicture(
|
||||
uri: entry.uri,
|
||||
mimeType: entry.mimeType,
|
||||
colorFilter: Constants.svgColorFilter,
|
||||
colorFilter: colorFilter,
|
||||
),
|
||||
width: extent,
|
||||
height: extent,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
return heroTag == null
|
||||
|
|
11
lib/widgets/common/borders.dart
Normal file
11
lib/widgets/common/borders.dart
Normal file
|
@ -0,0 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class AvesCircleBorder {
|
||||
static BoxBorder build(BuildContext context) {
|
||||
final subPixel = MediaQuery.of(context).devicePixelRatio > 2;
|
||||
return Border.all(
|
||||
color: Colors.white30,
|
||||
width: subPixel ? 0.5 : 1.0,
|
||||
);
|
||||
}
|
||||
}
|
17
lib/widgets/common/data_providers/settings_provider.dart
Normal file
17
lib/widgets/common/data_providers/settings_provider.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
import 'package:aves/model/settings.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class SettingsProvider extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const SettingsProvider({@required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChangeNotifierProvider<Settings>.value(
|
||||
value: settings,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:aves/model/image_entry.dart';
|
||||
import 'package:aves/utils/constants.dart';
|
||||
import 'package:aves/model/settings.dart';
|
||||
import 'package:aves/widgets/album/empty.dart';
|
||||
import 'package:aves/widgets/common/icons.dart';
|
||||
import 'package:aves/widgets/common/image_providers/thumbnail_provider.dart';
|
||||
|
@ -77,12 +77,13 @@ class ImageView extends StatelessWidget {
|
|||
|
||||
Widget child;
|
||||
if (entry.isSvg) {
|
||||
final colorFilter = ColorFilter.mode(Color(settings.svgBackground), BlendMode.dstOver);
|
||||
child = PhotoView.customChild(
|
||||
child: SvgPicture(
|
||||
UriPicture(
|
||||
uri: entry.uri,
|
||||
mimeType: entry.mimeType,
|
||||
colorFilter: Constants.svgColorFilter,
|
||||
colorFilter: colorFilter,
|
||||
),
|
||||
placeholderBuilder: (context) => loadingBuilder(context, fastThumbnailProvider),
|
||||
),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:aves/model/settings.dart';
|
||||
import 'package:aves/services/android_app_service.dart';
|
||||
import 'package:aves/widgets/common/action_delegates/map_style_dialog.dart';
|
||||
import 'package:aves/widgets/common/borders.dart';
|
||||
import 'package:aves/widgets/common/fx/blurred.dart';
|
||||
import 'package:aves/widgets/common/icons.dart';
|
||||
import 'package:aves/widgets/fullscreen/info/location_section.dart';
|
||||
|
@ -115,10 +116,10 @@ class MapOverlayButton extends StatelessWidget {
|
|||
return BlurredOval(
|
||||
child: Material(
|
||||
type: MaterialType.circle,
|
||||
color: FullscreenOverlay.backgroundColor,
|
||||
color: kOverlayBackgroundColor,
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
border: FullscreenOverlay.buildBorder(context),
|
||||
border: AvesCircleBorder.build(context),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: IconButton(
|
||||
|
|
|
@ -80,7 +80,7 @@ class _FullscreenBottomOverlayState extends State<FullscreenBottomOverlay> {
|
|||
final overlayContentMaxWidth = mqWidth - viewPadding.horizontal - innerPadding.horizontal;
|
||||
|
||||
return Container(
|
||||
color: FullscreenOverlay.backgroundColor,
|
||||
color: kOverlayBackgroundColor,
|
||||
padding: viewInsets + viewPadding.copyWith(top: 0),
|
||||
child: FutureBuilder<OverlayMetadata>(
|
||||
future: _detailLoader,
|
||||
|
|
|
@ -1,17 +1,8 @@
|
|||
import 'package:aves/widgets/common/borders.dart';
|
||||
import 'package:aves/widgets/common/fx/blurred.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FullscreenOverlay {
|
||||
static const backgroundColor = Colors.black26;
|
||||
|
||||
static BoxBorder buildBorder(BuildContext context) {
|
||||
final subPixel = MediaQuery.of(context).devicePixelRatio > 2;
|
||||
return Border.all(
|
||||
color: Colors.white30,
|
||||
width: subPixel ? 0.5 : 1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
const kOverlayBackgroundColor = Colors.black26;
|
||||
|
||||
class OverlayButton extends StatelessWidget {
|
||||
final Animation<double> scale;
|
||||
|
@ -26,10 +17,10 @@ class OverlayButton extends StatelessWidget {
|
|||
child: BlurredOval(
|
||||
child: Material(
|
||||
type: MaterialType.circle,
|
||||
color: FullscreenOverlay.backgroundColor,
|
||||
color: kOverlayBackgroundColor,
|
||||
child: Ink(
|
||||
decoration: BoxDecoration(
|
||||
border: FullscreenOverlay.buildBorder(context),
|
||||
border: AvesCircleBorder.build(context),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: child,
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:aves/model/image_entry.dart';
|
|||
import 'package:aves/services/android_app_service.dart';
|
||||
import 'package:aves/utils/durations.dart';
|
||||
import 'package:aves/utils/time_utils.dart';
|
||||
import 'package:aves/widgets/common/borders.dart';
|
||||
import 'package:aves/widgets/common/fx/blurred.dart';
|
||||
import 'package:aves/widgets/common/icons.dart';
|
||||
import 'package:aves/widgets/fullscreen/overlay/common.dart';
|
||||
|
@ -180,8 +181,8 @@ class VideoControlOverlayState extends State<VideoControlOverlay> with SingleTic
|
|||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 4, horizontal: 16) + EdgeInsets.only(bottom: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: FullscreenOverlay.backgroundColor,
|
||||
border: FullscreenOverlay.buildBorder(context),
|
||||
color: kOverlayBackgroundColor,
|
||||
border: AvesCircleBorder.build(context),
|
||||
borderRadius: BorderRadius.circular(progressBarBorderRadius),
|
||||
),
|
||||
child: Column(
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:aves/utils/constants.dart';
|
|||
import 'package:aves/widgets/common/data_providers/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/settings/coordinate_format.dart';
|
||||
import 'package:aves/widgets/settings/launch_page.dart';
|
||||
import 'package:aves/widgets/settings/svg_background.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SettingsPage extends StatelessWidget {
|
||||
|
@ -27,6 +28,14 @@ class SettingsPage extends StatelessWidget {
|
|||
Flexible(child: LaunchPageSelector()),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text('SVG background:'),
|
||||
SizedBox(width: 8),
|
||||
Flexible(child: SvgBackgroundSelector()),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
|
43
lib/widgets/settings/svg_background.dart
Normal file
43
lib/widgets/settings/svg_background.dart
Normal file
|
@ -0,0 +1,43 @@
|
|||
import 'package:aves/model/settings.dart';
|
||||
import 'package:aves/widgets/common/borders.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SvgBackgroundSelector extends StatefulWidget {
|
||||
@override
|
||||
_SvgBackgroundSelectorState createState() => _SvgBackgroundSelectorState();
|
||||
}
|
||||
|
||||
class _SvgBackgroundSelectorState extends State<SvgBackgroundSelector> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const radius = 24.0;
|
||||
return DropdownButton<int>(
|
||||
items: [0xFFFFFFFF, 0xFF000000, 0x00000000].map((selected) {
|
||||
return DropdownMenuItem(
|
||||
value: selected,
|
||||
child: Container(
|
||||
height: radius,
|
||||
width: radius,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(selected),
|
||||
border: AvesCircleBorder.build(context),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: selected == 0
|
||||
? Icon(
|
||||
Icons.clear,
|
||||
size: 20,
|
||||
color: Colors.white30,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
value: settings.svgBackground,
|
||||
onChanged: (selected) {
|
||||
settings.svgBackground = selected;
|
||||
setState(() {});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue