info: improved loading of location & metadata sections

This commit is contained in:
Thibault Deckers 2020-04-01 15:08:22 +09:00
parent 6feb1efb13
commit a5115fb83b
6 changed files with 145 additions and 57 deletions

View file

@ -1,7 +1,14 @@
import 'package:flutter/material.dart';
final Map<String, Color> _stringColors = {};
Color stringToColor(String string, {double saturation = .8, double lightness = .6}) {
final hash = string.codeUnits.fold(0, (prev, el) => prev = el + ((prev << 5) - prev));
final hue = (hash % 360).toDouble();
return HSLColor.fromAHSL(1.0, hue, saturation, lightness).toColor();
var color = _stringColors[string];
if (color == null) {
final hash = string.codeUnits.fold(0, (prev, el) => prev = el + ((prev << 5) - prev));
final hue = (hash % 360).toDouble();
color = HSLColor.fromAHSL(1.0, hue, saturation, lightness).toColor();
_stringColors[string] = color;
}
return color;
}

View file

@ -6,6 +6,7 @@ import 'package:aves/utils/android_app_service.dart';
import 'package:aves/utils/geo_utils.dart';
import 'package:aves/widgets/common/aves_filter_chip.dart';
import 'package:aves/widgets/fullscreen/info/info_page.dart';
import 'package:aves/widgets/fullscreen/info/map_initializer.dart';
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:outline_material_icons/outline_material_icons.dart';
@ -168,31 +169,34 @@ class ImageMapState extends State<ImageMap> with AutomaticKeepAliveClientMixin {
return Row(
children: [
Expanded(
child: SizedBox(
height: 200,
child: ClipRRect(
borderRadius: const BorderRadius.all(
Radius.circular(16),
),
child: GoogleMap(
initialCameraPosition: CameraPosition(
target: widget.latLng,
zoom: widget.initialZoom,
child: ClipRRect(
borderRadius: const BorderRadius.all(
Radius.circular(16),
),
child: Container(
color: Colors.white70,
height: 200,
child: GoogleMapInitializer(
builder: (context) => GoogleMap(
initialCameraPosition: CameraPosition(
target: widget.latLng,
zoom: widget.initialZoom,
),
onMapCreated: (controller) => setState(() => _controller = controller),
rotateGesturesEnabled: false,
scrollGesturesEnabled: false,
zoomGesturesEnabled: false,
tiltGesturesEnabled: false,
myLocationEnabled: false,
myLocationButtonEnabled: false,
markers: {
Marker(
markerId: MarkerId(widget.markerId),
icon: BitmapDescriptor.defaultMarkerWithHue(accentHue),
position: widget.latLng,
)
},
),
onMapCreated: (controller) => setState(() => _controller = controller),
rotateGesturesEnabled: false,
scrollGesturesEnabled: false,
zoomGesturesEnabled: false,
tiltGesturesEnabled: false,
myLocationEnabled: false,
myLocationButtonEnabled: false,
markers: {
Marker(
markerId: MarkerId(widget.markerId),
icon: BitmapDescriptor.defaultMarkerWithHue(accentHue),
position: widget.latLng,
)
},
),
),
),

View file

@ -0,0 +1,65 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:meta/meta.dart';
// workaround to Google Maps initialization blocking the dart thread
// cf https://github.com/flutter/flutter/issues/28493
// it loads first Google Maps in an isolate, and then build the desired map
class GoogleMapInitializer extends StatefulWidget {
final WidgetBuilder builder, errorBuilder, placeholderBuilder;
const GoogleMapInitializer({
@required this.builder,
this.errorBuilder,
this.placeholderBuilder,
});
@override
_GoogleMapInitializerState createState() => _GoogleMapInitializerState();
}
class _GoogleMapInitializerState extends State<GoogleMapInitializer> {
Future<void> initializer;
@override
void initState() {
super.initState();
initializer = compute(_preload, null);
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: initializer,
builder: (context, snapshot) {
if (snapshot.hasError) {
return widget.errorBuilder?.call(context) ?? const Icon(Icons.error_outline);
} else if (snapshot.connectionState == ConnectionState.done) {
return widget.builder(context);
} else {
return widget.placeholderBuilder?.call(context) ?? const SizedBox.shrink();
}
});
}
}
Future<void> _preload(_) async {
final mapCreatedCompleter = Completer();
GoogleMap(
compassEnabled: false,
mapToolbarEnabled: false,
rotateGesturesEnabled: false,
scrollGesturesEnabled: false,
zoomGesturesEnabled: false,
tiltGesturesEnabled: false,
buildingsEnabled: false,
initialCameraPosition: const CameraPosition(target: LatLng(0, 0), zoom: 20),
onMapCreated: (controller) => mapCreatedCompleter.complete(),
);
return mapCreatedCompleter.future;
}

View file

@ -6,6 +6,7 @@ import 'package:aves/model/metadata_service.dart';
import 'package:aves/utils/color_utils.dart';
import 'package:aves/widgets/common/fx/highlight_decoration.dart';
import 'package:aves/widgets/fullscreen/info/info_page.dart';
import 'package:expansion_tile_card/expansion_tile_card.dart';
import 'package:flutter/material.dart';
import 'package:outline_material_icons/outline_material_icons.dart';
@ -63,37 +64,36 @@ class _MetadataSectionSliverState extends State<MetadataSectionSliver> with Auto
Widget build(BuildContext context) {
super.build(context);
final directoriesWithoutTitle = _metadata.where((dir) => dir.name.isEmpty);
final directoriesWithTitle = _metadata.where((dir) => dir.name.isNotEmpty);
if (_metadata.isEmpty) return const SliverToBoxAdapter(child: SizedBox.shrink());
final directoriesWithoutTitle = _metadata.where((dir) => dir.name.isEmpty).toList();
final directoriesWithTitle = _metadata.where((dir) => dir.name.isNotEmpty).toList();
final untitledDirectoryCount = directoriesWithoutTitle.length;
return SliverList(
delegate: SliverChildListDelegate(
[
if (_metadata.isNotEmpty) const SectionRow(OMIcons.info),
...directoriesWithoutTitle.map((dir) => InfoRowGroup(dir.tags)),
Theme(
data: Theme.of(context).copyWith(cardColor: Colors.grey[900]),
child: ExpansionPanelList.radio(
key: ValueKey(widget.entry.uri),
expandedHeaderPadding: EdgeInsets.zero,
children: directoriesWithTitle.map<ExpansionPanelRadio>((dir) {
return ExpansionPanelRadio(
value: dir.name,
canTapOnHeader: true,
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(
title: _DirectoryTitle(dir.name),
);
},
body: Container(
alignment: Alignment.topLeft,
padding: const EdgeInsets.all(8),
child: InfoRowGroup(dir.tags),
),
);
}).toList(),
),
)
],
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index == 0) {
return const SectionRow(OMIcons.info);
}
if (index < untitledDirectoryCount + 1) {
final dir = directoriesWithoutTitle[index - 1];
return InfoRowGroup(dir.tags);
}
final dir = directoriesWithTitle[index - 1 - untitledDirectoryCount];
return ExpansionTileCard(
title: _DirectoryTitle(dir.name),
children: [
const Divider(thickness: 1.0, height: 1.0),
Container(
alignment: Alignment.topLeft,
padding: const EdgeInsets.all(8),
child: InfoRowGroup(dir.tags),
),
],
baseColor: Colors.grey[900],
);
},
childCount: 1 + _metadata.length,
),
);
}
@ -143,6 +143,7 @@ class _DirectoryTitle extends StatelessWidget {
child: Text(
name,
style: const TextStyle(
color: Colors.white,
shadows: [
Shadow(
color: Colors.black,
@ -153,6 +154,9 @@ class _DirectoryTitle extends StatelessWidget {
fontSize: 18,
fontFamily: 'Concourse Caps',
),
softWrap: false,
overflow: TextOverflow.fade,
maxLines: 1,
),
),
);

View file

@ -101,6 +101,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
expansion_tile_card:
dependency: "direct main"
description:
name: expansion_tile_card
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
flushbar:
dependency: "direct main"
description:

View file

@ -23,6 +23,7 @@ dependencies:
git:
url: git://github.com/deckerst/flutter-draggable-scrollbar.git
event_bus:
expansion_tile_card:
flushbar:
# flushbar-1.9.1 cannot be built with Flutter 1.15.17
git: