Skip to content

Commit

Permalink
Fixed bug where explicitly disabled stores would not be excluded from…
Browse files Browse the repository at this point in the history
… reading whilst browse caching

Improved example app capabilities (added ability to explictily disable stores when neccessary)
  • Loading branch information
JaffaKetchup committed Jan 4, 2025
1 parent 04423de commit 36dd576
Show file tree
Hide file tree
Showing 20 changed files with 478 additions and 272 deletions.
82 changes: 82 additions & 0 deletions example/lib/src/screens/main/map_view/components/attribution.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';

import '../map_view.dart';

class Attribution extends StatelessWidget {
const Attribution({
super.key,
required this.urlTemplate,
required this.mode,
required this.stores,
required this.otherStoresStrategy,
});

final String urlTemplate;
final MapViewMode mode;
final Map<String, BrowseStoreStrategy?> stores;
final BrowseStoreStrategy? otherStoresStrategy;

@override
Widget build(BuildContext context) => RichAttributionWidget(
alignment: AttributionAlignment.bottomLeft,
popupInitialDisplayDuration: const Duration(seconds: 3),
popupBorderRadius: BorderRadius.circular(12),
attributions: [
TextSourceAttribution(Uri.parse(urlTemplate).host),
const TextSourceAttribution(
'For demonstration purposes only',
prependCopyright: false,
textStyle: TextStyle(fontWeight: FontWeight.bold),
),
const TextSourceAttribution(
'Offline mapping made with FMTC',
prependCopyright: false,
textStyle: TextStyle(fontStyle: FontStyle.italic),
),
LogoSourceAttribution(
mode == MapViewMode.standard
? const Icon(Icons.bug_report)
: const SizedBox.shrink(),
tooltip: 'Show resolved store configuration',
onTap: () => showDialog(
context: context,
builder: (context) => AlertDialog.adaptive(
title: const Text('Resolved store configuration'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
stores.entries.isEmpty
? 'No stores set explicitly'
: stores.entries
.map(
(e) => '${e.key}: ${e.value ?? 'Explicitly '
'disabled'}',
)
.join('\n'),
),
Text(
otherStoresStrategy == null
? 'No other stores in use'
: 'All unspecified stores: $otherStoresStrategy',
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Understood'),
),
],
),
),
),
LogoSourceAttribution(
Image.asset('assets/icons/ProjectIcon.png'),
tooltip: 'flutter_map_tile_caching',
),
],
);
}
52 changes: 25 additions & 27 deletions example/lib/src/screens/main/map_view/map_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import '../../../shared/state/general_provider.dart';
import '../../../shared/state/region_selection_provider.dart';
import '../../../shared/state/selected_tab_state.dart';
import 'components/additional_overlay/additional_overlay.dart';
import 'components/attribution.dart';
import 'components/debugging_tile_builder/debugging_tile_builder.dart';
import 'components/download_progress/download_progress_masker.dart';
import 'components/recovery_regions/recovery_regions.dart';
Expand Down Expand Up @@ -264,6 +265,9 @@ class _MapViewState extends State<MapView> with TickerProviderStateMixin {
builder: (context, provider, _) {
final urlTemplate = provider.urlTemplate;

final otherStoresStrategy = provider.currentStores['(unspecified)']
?.toBrowseStoreStrategy();

final compiledStoreNames =
Map<String, BrowseStoreStrategy?>.fromEntries([
...stores.entries.where((e) => e.value == urlTemplate).map((e) {
Expand All @@ -276,37 +280,31 @@ class _MapViewState extends State<MapView> with TickerProviderStateMixin {
if (behaviour == null) return null;
return MapEntry(e.key, behaviour);
}).nonNulls,
...stores.entries
.where((e) => e.value != urlTemplate)
.map((e) => MapEntry(e.key, null)),
...stores.entries.where(
(e) {
if (e.value != urlTemplate) return true;

final internalBehaviour = provider.currentStores[e.key];
final behaviour = internalBehaviour == null
? provider.inheritableBrowseStoreStrategy
: internalBehaviour.toBrowseStoreStrategy(
provider.inheritableBrowseStoreStrategy,
);

return provider.explicitlyExcludedStores.contains(e.key) &&
behaviour == null &&
otherStoresStrategy != null;
},
).map((e) => MapEntry(e.key, null)),
]);

final attribution = RichAttributionWidget(
alignment: AttributionAlignment.bottomLeft,
popupInitialDisplayDuration: const Duration(seconds: 3),
popupBorderRadius: BorderRadius.circular(12),
attributions: [
TextSourceAttribution(Uri.parse(urlTemplate).host),
const TextSourceAttribution(
'For demonstration purposes only',
prependCopyright: false,
textStyle: TextStyle(fontWeight: FontWeight.bold),
),
const TextSourceAttribution(
'Offline mapping made with FMTC',
prependCopyright: false,
textStyle: TextStyle(fontStyle: FontStyle.italic),
),
LogoSourceAttribution(
Image.asset('assets/icons/ProjectIcon.png'),
tooltip: 'flutter_map_tile_caching',
),
],
final attribution = Attribution(
urlTemplate: urlTemplate,
mode: widget.mode,
stores: compiledStoreNames,
otherStoresStrategy: otherStoresStrategy,
);

final otherStoresStrategy = provider.currentStores['(unspecified)']
?.toBrowseStoreStrategy();

final tileLayer = TileLayer(
urlTemplate: urlTemplate,
userAgentPackageName: 'dev.jaffaketchup.fmtc.demo',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,74 +66,93 @@ class ColumnHeadersAndInheritableSettings extends StatelessWidget {
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 28),
child: Selector<GeneralProvider, BrowseStoreStrategy?>(
selector: (context, provider) =>
provider.inheritableBrowseStoreStrategy,
builder: (context, currentBehaviour, child) {
if (useCompactLayout) {
return Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: DropdownButton(
items: <BrowseStoreStrategy?>[null]
.followedBy(BrowseStoreStrategy.values)
.map(
(e) => DropdownMenuItem(
value: e,
alignment: Alignment.center,
child: switch (e) {
null => const Icon(
Icons.disabled_by_default_rounded,
),
BrowseStoreStrategy.read =>
const Icon(Icons.visibility),
BrowseStoreStrategy.readUpdate =>
const Icon(Icons.edit),
BrowseStoreStrategy.readUpdateCreate =>
const Icon(Icons.add),
},
),
)
.toList(),
value: currentBehaviour,
onChanged: (v) => context
.read<GeneralProvider>()
.inheritableBrowseStoreStrategy = v,
),
),
);
}
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: BrowseStoreStrategy.values.map(
(e) {
final value = currentBehaviour == e
? true
: InternalBrowseStoreStrategy.priority
.indexOf(currentBehaviour) <
InternalBrowseStoreStrategy.priority
.indexOf(e)
? false
: null;

return Checkbox.adaptive(
value: value,
onChanged: (v) => context
Row(
children: [
const SizedBox(width: 20),
Tooltip(
message: 'These inheritance options are tracked manually by\n'
'the app and not FMTC. This enables both inheritance\n'
'and "All unspecified" (which uses `otherStoresStrategy`\n'
'in FMTC) to be represented in the example app. Tap\n'
'the debug icon in the map attribution to see how the\n'
'store configuration is resolved and passed to FMTC.',
textAlign: TextAlign.center,
child: Icon(
Icons.help_outline,
color: Colors.black.withAlpha(255 ~/ 3),
),
),
const Spacer(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 28),
child: Selector<GeneralProvider, BrowseStoreStrategy?>(
selector: (context, provider) =>
provider.inheritableBrowseStoreStrategy,
builder: (context, currentBehaviour, child) {
if (useCompactLayout) {
return Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: DropdownButton(
items: <BrowseStoreStrategy?>[null]
.followedBy(BrowseStoreStrategy.values)
.map(
(e) => DropdownMenuItem(
value: e,
alignment: Alignment.center,
child: switch (e) {
null => const Icon(
Icons.disabled_by_default_rounded,
),
BrowseStoreStrategy.read =>
const Icon(Icons.visibility),
BrowseStoreStrategy.readUpdate =>
const Icon(Icons.edit),
BrowseStoreStrategy.readUpdateCreate =>
const Icon(Icons.add),
},
),
)
.toList(),
value: currentBehaviour,
onChanged: (v) => context
.read<GeneralProvider>()
.inheritableBrowseStoreStrategy =
v == null ? null : e,
tristate: true,
materialTapTargetSize: MaterialTapTargetSize.padded,
visualDensity: VisualDensity.comfortable,
.inheritableBrowseStoreStrategy = v,
),
),
);
},
).toList(growable: false),
);
},
),
}
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: BrowseStoreStrategy.values.map(
(e) {
final value = currentBehaviour == e
? true
: InternalBrowseStoreStrategy.priority
.indexOf(currentBehaviour) <
InternalBrowseStoreStrategy.priority
.indexOf(e)
? false
: null;

return Checkbox.adaptive(
value: value,
onChanged: (v) => context
.read<GeneralProvider>()
.inheritableBrowseStoreStrategy =
v == null ? null : e,
tristate: true,
materialTapTargetSize: MaterialTapTargetSize.padded,
visualDensity: VisualDensity.comfortable,
);
},
).toList(growable: false),
);
},
),
),
],
),
const Divider(height: 8, indent: 12, endIndent: 12),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:provider/provider.dart';
import 'package:share_plus/share_plus.dart';

import '../../state/export_selection_provider.dart';
import 'example_app_limitations_text.dart';

part 'name_input_dialog.dart';
part 'progress_dialog.dart';
Expand Down Expand Up @@ -48,10 +49,7 @@ class ExportStoresButton extends StatelessWidget {
),
const SizedBox(height: 24),
Text(
'Within the example app, for simplicity, each store contains '
'tiles from a single URL template. Additionally, only one tile '
'layer with a single URL template can be used at any one time. '
'These are not limitations with FMTC.',
exampleAppLimitationsText,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.labelSmall,
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const exampleAppLimitationsText =
'There are some limitations to the example app which do not exist in FMTC, '
'because it is difficult to express in this UI design.\nEach store only '
'contains tiles from a single URL template. Only a single tile layer is '
'used/available (only a single URL template can be used at any one time).';
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';

import '../../../../../../../import/import.dart';
import '../../../../../../../store_editor/store_editor.dart';
import 'export_stores/example_app_limitations_text.dart';

class NewStoreButton extends StatelessWidget {
const NewStoreButton({super.key});
Expand Down Expand Up @@ -36,10 +37,7 @@ class NewStoreButton extends StatelessWidget {
),
const SizedBox(height: 24),
Text(
'Within the example app, for simplicity, each store contains '
'tiles from a single URL template. Additionally, only one tile '
'layer with a single URL template can be used at any one time. '
'These are not limitations with FMTC.',
exampleAppLimitationsText,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.labelSmall,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';

import '../../../../../../../import/import.dart';
import '../../../../../../../store_editor/store_editor.dart';
import 'export_stores/example_app_limitations_text.dart';

class NoStores extends StatelessWidget {
const NoStores({super.key});
Expand Down Expand Up @@ -50,10 +51,7 @@ class NoStores extends StatelessWidget {
),
const SizedBox(height: 32),
Text(
'Within the example app, for simplicity, each store contains '
'tiles from a single URL template. Additionally, only one '
'tile layer with a single URL template can be used at any '
'one time. These are not limitations with FMTC.',
exampleAppLimitationsText,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.labelSmall,
),
Expand Down
Loading

0 comments on commit 36dd576

Please sign in to comment.