aves/lib/widgets/fullscreen/info/maps/marker.dart
2020-09-26 11:41:53 +09:00

182 lines
5.2 KiB
Dart

import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:aves/model/image_entry.dart';
import 'package:aves/widgets/collection/thumbnail/raster.dart';
import 'package:aves/widgets/collection/thumbnail/vector.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class ImageMarker extends StatelessWidget {
final ImageEntry entry;
final double extent;
final Size pointerSize;
static const double outerBorderRadiusDim = 8;
static const double outerBorderWidth = 1.5;
static const double innerBorderWidth = 2;
static const outerBorderColor = Colors.white30;
static final innerBorderColor = Colors.grey[900];
const ImageMarker({
@required this.entry,
@required this.extent,
this.pointerSize = Size.zero,
});
@override
Widget build(BuildContext context) {
final thumbnail = entry.isSvg
? ThumbnailVectorImage(
entry: entry,
extent: extent,
)
: ThumbnailRasterImage(
entry: entry,
extent: extent,
);
final outerBorderRadius = BorderRadius.circular(outerBorderRadiusDim);
final innerBorderRadius = BorderRadius.circular(outerBorderRadiusDim - outerBorderWidth);
return CustomPaint(
foregroundPainter: MarkerPointerPainter(
color: innerBorderColor,
outlineColor: outerBorderColor,
outlineWidth: outerBorderWidth,
size: pointerSize,
),
child: Padding(
padding: EdgeInsets.only(bottom: pointerSize.height),
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: outerBorderColor,
width: outerBorderWidth,
),
borderRadius: outerBorderRadius,
),
child: DecoratedBox(
decoration: BoxDecoration(
border: Border.all(
color: innerBorderColor,
width: innerBorderWidth,
),
borderRadius: innerBorderRadius,
),
position: DecorationPosition.foreground,
child: ClipRRect(
borderRadius: innerBorderRadius,
child: thumbnail,
),
),
),
),
);
}
}
class MarkerPointerPainter extends CustomPainter {
final Color color, outlineColor;
final double outlineWidth;
final Size size;
const MarkerPointerPainter({
this.color,
this.outlineColor,
this.outlineWidth,
this.size,
});
@override
void paint(Canvas canvas, Size size) {
final pointerWidth = this.size.width;
final pointerHeight = this.size.height;
final bottomCenter = Offset(size.width / 2, size.height);
final topLeft = bottomCenter + Offset(-pointerWidth / 2, -pointerHeight);
final topRight = bottomCenter + Offset(pointerWidth / 2, -pointerHeight);
canvas.drawPath(
Path()
..moveTo(bottomCenter.dx, bottomCenter.dy)
..lineTo(topRight.dx, topRight.dy)
..lineTo(topLeft.dx, topLeft.dy)
..close(),
Paint()..color = outlineColor);
canvas.translate(0, -outlineWidth.ceilToDouble());
canvas.drawPath(
Path()
..moveTo(bottomCenter.dx, bottomCenter.dy)
..lineTo(topRight.dx, topRight.dy)
..lineTo(topLeft.dx, topLeft.dy)
..close(),
Paint()..color = color);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
// generate bitmap from widget, for Google Maps
class MarkerGeneratorWidget extends StatefulWidget {
final List<Widget> markers;
final Duration delay;
final Function(List<Uint8List> bitmaps) onComplete;
const MarkerGeneratorWidget({
Key key,
@required this.markers,
this.delay = Duration.zero,
@required this.onComplete,
}) : super(key: key);
@override
_MarkerGeneratorWidgetState createState() => _MarkerGeneratorWidgetState();
}
class _MarkerGeneratorWidgetState extends State<MarkerGeneratorWidget> {
final _globalKeys = <GlobalKey>[];
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (widget.delay > Duration.zero) {
await Future.delayed(widget.delay);
}
widget.onComplete(await _getBitmaps(context));
});
}
@override
Widget build(BuildContext context) {
return Transform.translate(
offset: Offset(MediaQuery.of(context).size.width, 0),
child: Material(
type: MaterialType.transparency,
child: Stack(
children: widget.markers.map((i) {
final key = GlobalKey();
_globalKeys.add(key);
return RepaintBoundary(
key: key,
child: i,
);
}).toList(),
),
),
);
}
Future<List<Uint8List>> _getBitmaps(BuildContext context) async {
final pixelRatio = MediaQuery.of(context).devicePixelRatio;
return Future.wait(_globalKeys.map((key) async {
RenderRepaintBoundary boundary = key.currentContext.findRenderObject();
final image = await boundary.toImage(pixelRatio: pixelRatio);
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
return byteData.buffer.asUint8List();
}));
}
}