diff --git a/CHANGELOG.md b/CHANGELOG.md index 6149d74df..9d28beea3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ OpenGL ES 3.0. * Renamed the method channel to `plugins.flutter.io/maplibre_gl_*` in all packages. +### Other changes +* Updated example app + ## 0.19.0 This is the first version where all packages are published on pub.dev. Please diff --git a/example/README.md b/example/README.md deleted file mode 100644 index 5cc0e144e..000000000 --- a/example/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# maplibre_gl_example - -Demonstrates how to use the maplibre_gl plugin. - -## Getting Started - -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Lab: Write your first Flutter app](https://flutter.io/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.io/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.io/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/example/lib/place_circle.dart b/example/lib/annotation_circle_page.dart similarity index 50% rename from example/lib/place_circle.dart rename to example/lib/annotation_circle_page.dart index 04a66501a..aa54b3d82 100644 --- a/example/lib/place_circle.dart +++ b/example/lib/annotation_circle_page.dart @@ -7,29 +7,17 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; -import 'page.dart'; - -class PlaceCirclePage extends ExamplePage { - const PlaceCirclePage({super.key}) - : super(const Icon(Icons.check_circle), 'Place circle'); - - @override - Widget build(BuildContext context) { - return const PlaceCircleBody(); - } -} - -class PlaceCircleBody extends StatefulWidget { - const PlaceCircleBody({super.key}); +class AnnotationCirclePage extends StatefulWidget { + const AnnotationCirclePage({super.key}); @override - State createState() => PlaceCircleBodyState(); + State createState() => _AnnotationCirclePageState(); } -class PlaceCircleBodyState extends State { - PlaceCircleBodyState(); - +class _AnnotationCirclePageState extends State { static const LatLng center = LatLng(-33.86711, 151.1947171); MaplibreMapController? controller; @@ -188,14 +176,76 @@ class PlaceCircleBodyState extends State { @override Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Center( - child: SizedBox( - width: 300.0, - height: 200.0, + return ExampleScaffold( + page: ExamplePage.annotationCircle, + body: Column( + children: [ + Wrap( + alignment: WrapAlignment.center, + children: [ + TextButton( + onPressed: (_circleCount == 12) ? null : _add, + child: const Text('add'), + ), + TextButton( + onPressed: (_selectedCircle == null) ? null : _remove, + child: const Text('remove'), + ), + ], + ), + Wrap( + alignment: WrapAlignment.center, + children: [ + TextButton( + onPressed: + (_selectedCircle == null) ? null : _changeCircleOpacity, + child: const Text('change circle-opacity'), + ), + TextButton( + onPressed: + (_selectedCircle == null) ? null : _changeCircleRadius, + child: const Text('change circle-radius'), + ), + TextButton( + onPressed: + (_selectedCircle == null) ? null : _changeCircleColor, + child: const Text('change circle-color'), + ), + TextButton( + onPressed: (_selectedCircle == null) ? null : _changeCircleBlur, + child: const Text('change circle-blur'), + ), + TextButton( + onPressed: + (_selectedCircle == null) ? null : _changeCircleStrokeWidth, + child: const Text('change circle-stroke-width'), + ), + TextButton( + onPressed: + (_selectedCircle == null) ? null : _changeCircleStrokeColor, + child: const Text('change circle-stroke-color'), + ), + TextButton( + onPressed: (_selectedCircle == null) + ? null + : _changeCircleStrokeOpacity, + child: const Text('change circle-stroke-opacity'), + ), + TextButton( + onPressed: (_selectedCircle == null) ? null : _changePosition, + child: const Text('change position'), + ), + TextButton( + onPressed: (_selectedCircle == null) ? null : _changeDraggable, + child: const Text('toggle draggable'), + ), + TextButton( + onPressed: (_selectedCircle == null) ? null : _getLatLng, + child: const Text('get current LatLng'), + ), + ], + ), + Expanded( child: MaplibreMap( onMapCreated: _onMapCreated, initialCameraPosition: const CameraPosition( @@ -204,96 +254,8 @@ class PlaceCircleBodyState extends State { ), ), ), - ), - Expanded( - child: SingleChildScrollView( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Row( - children: [ - Column( - children: [ - TextButton( - onPressed: (_circleCount == 12) ? null : _add, - child: const Text('add'), - ), - TextButton( - onPressed: (_selectedCircle == null) ? null : _remove, - child: const Text('remove'), - ), - ], - ), - Column( - children: [ - TextButton( - onPressed: (_selectedCircle == null) - ? null - : _changeCircleOpacity, - child: const Text('change circle-opacity'), - ), - TextButton( - onPressed: (_selectedCircle == null) - ? null - : _changeCircleRadius, - child: const Text('change circle-radius'), - ), - TextButton( - onPressed: (_selectedCircle == null) - ? null - : _changeCircleColor, - child: const Text('change circle-color'), - ), - TextButton( - onPressed: (_selectedCircle == null) - ? null - : _changeCircleBlur, - child: const Text('change circle-blur'), - ), - TextButton( - onPressed: (_selectedCircle == null) - ? null - : _changeCircleStrokeWidth, - child: const Text('change circle-stroke-width'), - ), - TextButton( - onPressed: (_selectedCircle == null) - ? null - : _changeCircleStrokeColor, - child: const Text('change circle-stroke-color'), - ), - TextButton( - onPressed: (_selectedCircle == null) - ? null - : _changeCircleStrokeOpacity, - child: const Text('change circle-stroke-opacity'), - ), - TextButton( - onPressed: (_selectedCircle == null) - ? null - : _changePosition, - child: const Text('change position'), - ), - TextButton( - onPressed: (_selectedCircle == null) - ? null - : _changeDraggable, - child: const Text('toggle draggable'), - ), - TextButton( - onPressed: - (_selectedCircle == null) ? null : _getLatLng, - child: const Text('get current LatLng'), - ), - ], - ), - ], - ) - ], - ), - ), - ), - ], + ], + ), ); } } diff --git a/example/lib/place_fill.dart b/example/lib/annotation_fill_page.dart similarity index 55% rename from example/lib/place_fill.dart rename to example/lib/annotation_fill_page.dart index b37470298..cd4241544 100644 --- a/example/lib/place_fill.dart +++ b/example/lib/annotation_fill_page.dart @@ -7,30 +7,17 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; -import 'page.dart'; - -class PlaceFillPage extends ExamplePage { - const PlaceFillPage({super.key}) - : super(const Icon(Icons.check_circle), 'Place fill'); - - @override - Widget build(BuildContext context) { - return const PlaceFillBody(); - } -} - -class PlaceFillBody extends StatefulWidget { - const PlaceFillBody({super.key}); +class AnnotationFillPage extends StatefulWidget { + const AnnotationFillPage({super.key}); @override - State createState() => PlaceFillBodyState(); + State createState() => _AnnotationFillPageState(); } -class PlaceFillBodyState extends State { - PlaceFillBodyState(); - - static const LatLng center = LatLng(-33.86711, 151.1947171); +class _AnnotationFillPageState extends State { final String _fillPatternImage = "assets/fill/cat_silhouette_pattern.png"; final List> _defaultGeometry = [ @@ -59,12 +46,14 @@ class PlaceFillBodyState extends State { this.controller!.onFeatureDrag.add(_onFeatureDrag); } - void _onFeatureDrag(id, - {required current, - required delta, - required origin, - required point, - required eventType}) { + void _onFeatureDrag( + id, { + required current, + required delta, + required origin, + required point, + required eventType, + }) { DragEventType type = eventType; switch (type) { case DragEventType.start: @@ -186,14 +175,54 @@ class PlaceFillBodyState extends State { @override Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Center( - child: SizedBox( - width: 300.0, - height: 200.0, + return ExampleScaffold( + page: ExamplePage.annotationFill, + body: Column( + children: [ + Wrap( + alignment: WrapAlignment.center, + children: [ + TextButton( + onPressed: (_fillCount == 12) ? null : _add, + child: const Text('add'), + ), + TextButton( + onPressed: (_selectedFill == null) ? null : _remove, + child: const Text('remove'), + ), + ], + ), + Wrap( + alignment: WrapAlignment.center, + children: [ + TextButton( + onPressed: (_selectedFill == null) ? null : _changeFillOpacity, + child: const Text('change fill-opacity'), + ), + TextButton( + onPressed: (_selectedFill == null) ? null : _changeFillColor, + child: const Text('change fill-color'), + ), + TextButton( + onPressed: + (_selectedFill == null) ? null : _changeFillOutlineColor, + child: const Text('change fill-outline-color'), + ), + TextButton( + onPressed: (_selectedFill == null) ? null : _changeFillPattern, + child: const Text('change fill-pattern'), + ), + TextButton( + onPressed: (_selectedFill == null) ? null : _changePosition, + child: const Text('change position'), + ), + TextButton( + onPressed: (_selectedFill == null) ? null : _changeDraggable, + child: const Text('toggle draggable'), + ), + ], + ), + Expanded( child: MaplibreMap( onMapCreated: _onMapCreated, onStyleLoadedCallback: _onStyleLoaded, @@ -203,70 +232,8 @@ class PlaceFillBodyState extends State { ), ), ), - ), - Expanded( - child: SingleChildScrollView( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Row( - children: [ - Column( - children: [ - TextButton( - onPressed: (_fillCount == 12) ? null : _add, - child: const Text('add'), - ), - TextButton( - onPressed: (_selectedFill == null) ? null : _remove, - child: const Text('remove'), - ), - ], - ), - Column( - children: [ - TextButton( - onPressed: (_selectedFill == null) - ? null - : _changeFillOpacity, - child: const Text('change fill-opacity'), - ), - TextButton( - onPressed: - (_selectedFill == null) ? null : _changeFillColor, - child: const Text('change fill-color'), - ), - TextButton( - onPressed: (_selectedFill == null) - ? null - : _changeFillOutlineColor, - child: const Text('change fill-outline-color'), - ), - TextButton( - onPressed: (_selectedFill == null) - ? null - : _changeFillPattern, - child: const Text('change fill-pattern'), - ), - TextButton( - onPressed: - (_selectedFill == null) ? null : _changePosition, - child: const Text('change position'), - ), - TextButton( - onPressed: - (_selectedFill == null) ? null : _changeDraggable, - child: const Text('toggle draggable'), - ), - ], - ), - ], - ) - ], - ), - ), - ), - ], + ], + ), ); } } diff --git a/example/lib/layer.dart b/example/lib/annotation_layer_page.dart similarity index 62% rename from example/lib/layer.dart rename to example/lib/annotation_layer_page.dart index c3b0c9cb3..04aee9cae 100644 --- a/example/lib/layer.dart +++ b/example/lib/annotation_layer_page.dart @@ -3,25 +3,19 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; -import 'package:maplibre_gl_example/page.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; +import 'package:maplibre_gl_example/main.dart'; -import 'util.dart'; +import 'common/util.dart'; -class LayerPage extends ExamplePage { - const LayerPage({super.key}) : super(const Icon(Icons.share), 'Layer'); +class AnnotationLayerPage extends StatefulWidget { + const AnnotationLayerPage({super.key}); @override - Widget build(BuildContext context) => const LayerBody(); + State createState() => _AnnotationLayerPageState(); } -class LayerBody extends StatefulWidget { - const LayerBody({super.key}); - - @override - State createState() => LayerState(); -} - -class LayerState extends State { +class _AnnotationLayerPageState extends State { static const LatLng center = LatLng(-33.86711, 151.1947171); late MaplibreMapController controller; @@ -39,104 +33,115 @@ class LayerState extends State { @override Widget build(BuildContext context) { - return ListView(children: [ - Center( - child: SizedBox( - height: 400.0, - child: MaplibreMap( - dragEnabled: false, - myLocationEnabled: true, - onMapCreated: _onMapCreated, - onMapClick: (point, latLong) => - debugPrint(point.toString() + latLong.toString()), - onStyleLoadedCallback: _onStyleLoadedCallback, - initialCameraPosition: const CameraPosition( - target: center, - zoom: 11.0, - ), - annotationOrder: const [], - )), - ), - TextButton( - onPressed: () { - controller - .setLayerProperties( - "lines", - LineLayerProperties.fromJson( - {"visibility": linesVisible ? "none" : "visible"})) - .then((value) => setState(() => linesVisible = !linesVisible)); - }, - child: const Text('toggle line visibility'), - ), - TextButton( - onPressed: () { - controller - .setLayerProperties( - "lines", - LineLayerProperties.fromJson( - {"line-color": linesRed ? "#0000ff" : "#ff0000"})) - .then((value) => setState(() => linesRed = !linesRed)); - }, - child: const Text('toggle line color'), - ), - TextButton( - onPressed: () { - controller - .setLayerProperties( - "fills", - FillLayerProperties.fromJson( - {"visibility": fillsVisible ? "none" : "visible"})) - .then((value) => setState(() => fillsVisible = !fillsVisible)); - }, - child: const Text('toggle fill visibility'), - ), - TextButton( - onPressed: () { - controller - .setLayerProperties( - "fills", - FillLayerProperties.fromJson( - {"fill-color": fillsRed ? "#0000ff" : "#ff0000"})) - .then((value) => setState(() => fillsRed = !fillsRed)); - }, - child: const Text('toggle fill color'), - ), - TextButton( - onPressed: () { - controller - .setLayerProperties( - "circles", - CircleLayerProperties.fromJson( - {"visibility": circlesVisible ? "none" : "visible"})) - .then( - (value) => setState(() => circlesVisible = !circlesVisible)); - }, - child: const Text('toggle circle visibility'), - ), - TextButton( - onPressed: () { - controller - .setLayerProperties( - "circles", - CircleLayerProperties.fromJson( - {"circle-color": circlesRed ? "#0000ff" : "#ff0000"})) - .then((value) => setState(() => circlesRed = !circlesRed)); - }, - child: const Text('toggle circle color'), - ), - TextButton( - onPressed: () { - controller - .setLayerProperties( - "symbols", - SymbolLayerProperties.fromJson( - {"visibility": symbolsVisible ? "none" : "visible"})) - .then( - (value) => setState(() => symbolsVisible = !symbolsVisible)); - }, - child: const Text('toggle (non-moving) symbols visibility'), - ), - ]); + return ExampleScaffold( + page: ExamplePage.annotationLayer, + body: Column(children: [ + Wrap( + alignment: WrapAlignment.center, + children: [ + TextButton( + onPressed: () { + controller + .setLayerProperties( + "lines", + LineLayerProperties.fromJson( + {"visibility": linesVisible ? "none" : "visible"})) + .then((value) => + setState(() => linesVisible = !linesVisible)); + }, + child: const Text('toggle line visibility'), + ), + TextButton( + onPressed: () { + controller + .setLayerProperties( + "lines", + LineLayerProperties.fromJson( + {"line-color": linesRed ? "#0000ff" : "#ff0000"})) + .then((value) => setState(() => linesRed = !linesRed)); + }, + child: const Text('toggle line color'), + ), + TextButton( + onPressed: () { + controller + .setLayerProperties( + "fills", + FillLayerProperties.fromJson( + {"visibility": fillsVisible ? "none" : "visible"})) + .then((value) => + setState(() => fillsVisible = !fillsVisible)); + }, + child: const Text('toggle fill visibility'), + ), + TextButton( + onPressed: () { + controller + .setLayerProperties( + "fills", + FillLayerProperties.fromJson( + {"fill-color": fillsRed ? "#0000ff" : "#ff0000"})) + .then((value) => setState(() => fillsRed = !fillsRed)); + }, + child: const Text('toggle fill color'), + ), + TextButton( + onPressed: () { + controller + .setLayerProperties( + "circles", + CircleLayerProperties.fromJson({ + "visibility": circlesVisible ? "none" : "visible" + })) + .then((value) => + setState(() => circlesVisible = !circlesVisible)); + }, + child: const Text('toggle circle visibility'), + ), + TextButton( + onPressed: () { + controller + .setLayerProperties( + "circles", + CircleLayerProperties.fromJson({ + "circle-color": circlesRed ? "#0000ff" : "#ff0000" + })) + .then((value) => setState(() => circlesRed = !circlesRed)); + }, + child: const Text('toggle circle color'), + ), + TextButton( + onPressed: () { + controller + .setLayerProperties( + "symbols", + SymbolLayerProperties.fromJson({ + "visibility": symbolsVisible ? "none" : "visible" + })) + .then((value) => + setState(() => symbolsVisible = !symbolsVisible)); + }, + child: const Text('toggle (non-moving) symbols visibility'), + ), + ], + ), + Expanded( + child: MaplibreMap( + dragEnabled: false, + myLocationEnabled: true, + onMapCreated: _onMapCreated, + onMapClick: (point, latLong) => + debugPrint('onMapClick: $point, $latLong'), + onStyleLoadedCallback: _onStyleLoadedCallback, + initialCameraPosition: const CameraPosition( + target: center, + zoom: 11.0, + ), + annotationOrder: const [], + ), + ), + ]), + ); } void _onMapCreated(MaplibreMapController controller) { diff --git a/example/lib/line.dart b/example/lib/annotation_line_page.dart similarity index 53% rename from example/lib/line.dart rename to example/lib/annotation_line_page.dart index a16a20009..b70c896fd 100644 --- a/example/lib/line.dart +++ b/example/lib/annotation_line_page.dart @@ -7,29 +7,18 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; -import 'page.dart'; - -class LinePage extends ExamplePage { - const LinePage({super.key}) : super(const Icon(Icons.share), 'Line'); - - @override - Widget build(BuildContext context) { - return const LineBody(); - } -} - -class LineBody extends StatefulWidget { - const LineBody({super.key}); +class AnnotationLinePage extends StatefulWidget { + const AnnotationLinePage({super.key}); @override - State createState() => LineBodyState(); + State createState() => _AnnotationLinePageState(); } -class LineBodyState extends State { - LineBodyState(); - - static const LatLng center = LatLng(-33.86711, 151.1947171); +class _AnnotationLinePageState extends State { + _AnnotationLinePageState(); MaplibreMapController? controller; int _lineCount = 0; @@ -147,89 +136,74 @@ class LineBodyState extends State { @override Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Center( - child: SizedBox( - height: 400.0, + return ExampleScaffold( + page: ExamplePage.annotationLine, + body: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Wrap( + alignment: WrapAlignment.center, + children: [ + TextButton( + onPressed: (_lineCount == 12) ? null : _add, + child: const Text('add'), + ), + TextButton( + onPressed: (_selectedLine == null) ? null : _remove, + child: const Text('remove'), + ), + TextButton( + onPressed: (_selectedLine == null) + ? null + : () async { + await _move(); + }, + child: const Text('move'), + ), + TextButton( + onPressed: (_selectedLine == null) ? null : _changeLinePattern, + child: const Text('change line-pattern'), + ), + ], + ), + Wrap( + alignment: WrapAlignment.center, + children: [ + TextButton( + onPressed: (_selectedLine == null) ? null : _changeAlpha, + child: const Text('change alpha'), + ), + TextButton( + onPressed: (_selectedLine == null) ? null : _toggleVisible, + child: const Text('toggle visible'), + ), + TextButton( + onPressed: (_selectedLine == null) + ? null + : () async { + var latLngs = + await controller!.getLineLatLngs(_selectedLine!); + for (var latLng in latLngs) { + debugPrint(latLng.toString()); + } + }, + child: const Text('print current LatLng'), + ), + ], + ), + Expanded( child: MaplibreMap( onMapCreated: _onMapCreated, onStyleLoadedCallback: _onStyleLoadedCallback, initialCameraPosition: const CameraPosition( - target: LatLng(-33.852, 151.211), - zoom: 11.0, + target: LatLng(-33, 151), + zoom: 7, ), ), ), - ), - Expanded( - child: SingleChildScrollView( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Column( - children: [ - Row( - children: [ - TextButton( - onPressed: (_lineCount == 12) ? null : _add, - child: const Text('add'), - ), - TextButton( - onPressed: (_selectedLine == null) ? null : _remove, - child: const Text('remove'), - ), - TextButton( - onPressed: (_selectedLine == null) - ? null - : () async { - await _move(); - }, - child: const Text('move'), - ), - TextButton( - onPressed: (_selectedLine == null) - ? null - : _changeLinePattern, - child: const Text('change line-pattern'), - ), - ], - ), - Row( - children: [ - TextButton( - onPressed: - (_selectedLine == null) ? null : _changeAlpha, - child: const Text('change alpha'), - ), - TextButton( - onPressed: - (_selectedLine == null) ? null : _toggleVisible, - child: const Text('toggle visible'), - ), - TextButton( - onPressed: (_selectedLine == null) - ? null - : () async { - var latLngs = await controller! - .getLineLatLngs(_selectedLine!); - for (var latLng in latLngs) { - debugPrint(latLng.toString()); - } - }, - child: const Text('print current LatLng'), - ), - ], - ), - ], - ), - ], - ), - ), - ), - ], + ], + ), ); } } diff --git a/example/lib/annotation_order_maps.dart b/example/lib/annotation_order_page.dart similarity index 63% rename from example/lib/annotation_order_maps.dart rename to example/lib/annotation_order_page.dart index 37cd81ff0..2d3f87abe 100644 --- a/example/lib/annotation_order_maps.dart +++ b/example/lib/annotation_order_page.dart @@ -1,26 +1,18 @@ import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; // ignore: unnecessary_import import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; +import 'package:maplibre_gl_example/main.dart'; -import 'page.dart'; -import 'util.dart'; +import 'common/util.dart'; -class AnnotationOrderPage extends ExamplePage { - const AnnotationOrderPage({super.key}) - : super(const Icon(Icons.layers), 'Annotation order maps'); +class AnnotationOrderPage extends StatefulWidget { + const AnnotationOrderPage({super.key}); @override - Widget build(BuildContext context) => const AnnotationOrderBody(); + State createState() => _AnnotationOrderPageState(); } -class AnnotationOrderBody extends StatefulWidget { - const AnnotationOrderBody({super.key}); - - @override - State createState() => _AnnotationOrderBodyState(); -} - -class _AnnotationOrderBodyState extends State { +class _AnnotationOrderPageState extends State { late MaplibreMapController controllerOne; late MaplibreMapController controllerTwo; @@ -28,22 +20,21 @@ class _AnnotationOrderBodyState extends State { @override Widget build(BuildContext context) { - return Column( - children: [ - Card( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 0.0), - child: Column( - children: [ - const Padding( - padding: EdgeInsets.only(bottom: 5.0), - child: Text( - 'This map has polygones (fill) above all other anotations (default behavior)'), - ), - Center( - child: SizedBox( - width: 250.0, - height: 250.0, + return ExampleScaffold( + page: ExamplePage.annotationOrder, + body: Column( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 0.0), + child: Column( + children: [ + const Padding( + padding: EdgeInsets.symmetric(vertical: 5.0), + child: Text( + 'This map has polygons (fill) above all other annotations (default behavior)'), + ), + Flexible( child: MaplibreMap( onMapCreated: onMapCreatedOne, onStyleLoadedCallback: () => onStyleLoaded(controllerOne), @@ -59,25 +50,21 @@ class _AnnotationOrderBodyState extends State { ], ), ), - ), - ], + ], + ), ), ), - ), - Card( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 0.0), - child: Column( - children: [ - const Padding( - padding: EdgeInsets.only(bottom: 5.0, top: 5.0), - child: Text( - 'This map has polygones (fill) under all other anotations'), - ), - Center( - child: SizedBox( - width: 250.0, - height: 250.0, + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 0.0), + child: Column( + children: [ + const Padding( + padding: EdgeInsets.only(bottom: 5.0, top: 5.0), + child: Text( + 'This map has polygones (fill) under all other anotations'), + ), + Flexible( child: MaplibreMap( onMapCreated: onMapCreatedTwo, onStyleLoadedCallback: () => onStyleLoaded(controllerTwo), @@ -93,12 +80,12 @@ class _AnnotationOrderBodyState extends State { ], ), ), - ), - ], + ], + ), ), ), - ), - ], + ], + ), ); } diff --git a/example/lib/annotation_source_page.dart b/example/lib/annotation_source_page.dart new file mode 100644 index 000000000..42f25e749 --- /dev/null +++ b/example/lib/annotation_source_page.dart @@ -0,0 +1,165 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; + +class AnnotationSourcePage extends StatefulWidget { + const AnnotationSourcePage({super.key}); + + @override + State createState() => _AnnotationSourcePageState(); +} + +class _AnnotationSourcePageState extends State { + _AnnotationSourcePageState(); + + static const sourceId = 'sydney_source'; + static const layerId = 'sydney_layer'; + + bool sourceAdded = false; + bool layerAdded = false; + bool imageFlag = false; + late MaplibreMapController controller; + + void _onMapCreated(MaplibreMapController controller) { + this.controller = controller; + } + + @override + void dispose() { + super.dispose(); + } + + /// Adds an asset image as a source to the currently displayed style + Future addImageSourceFromAsset( + String imageSourceId, String assetName) async { + final ByteData bytes = await rootBundle.load(assetName); + final Uint8List list = bytes.buffer.asUint8List(); + return controller.addImageSource( + imageSourceId, + list, + const LatLngQuad( + bottomRight: LatLng(-33.86264728692581, 151.19916915893555), + bottomLeft: LatLng(-33.86264728692581, 151.2288236618042), + topLeft: LatLng(-33.84322353475214, 151.2288236618042), + topRight: LatLng(-33.84322353475214, 151.19916915893555), + ), + ); + } + + Future removeImageSource(String imageSourceId) { + return controller.removeSource(imageSourceId); + } + + Future addLayer(String imageLayerId, String imageSourceId) { + if (layerAdded) { + removeLayer(imageLayerId); + } + setState(() => layerAdded = true); + return controller.addImageLayer(imageLayerId, imageSourceId); + } + + Future addLayerBelow( + String imageLayerId, String imageSourceId, String belowLayerId) { + if (layerAdded) { + removeLayer(imageLayerId); + } + setState(() => layerAdded = true); + return controller.addImageLayerBelow( + imageLayerId, imageSourceId, belowLayerId); + } + + Future removeLayer(String imageLayerId) { + setState(() => layerAdded = false); + return controller.removeLayer(imageLayerId); + } + + Future updateImageSourceFromAsset( + String imageSourceId, String assetName) async { + final ByteData bytes = await rootBundle.load(assetName); + final Uint8List list = bytes.buffer.asUint8List(); + return controller.updateImageSource(imageSourceId, list, null); + } + + String pickImage() { + return imageFlag ? 'assets/sydney0.png' : 'assets/sydney1.png'; + } + + @override + Widget build(BuildContext context) { + return ExampleScaffold( + page: ExamplePage.annotationSource, + body: Column( + children: [ + Wrap( + alignment: WrapAlignment.center, + children: [ + TextButton( + onPressed: sourceAdded + ? null + : () { + addImageSourceFromAsset(sourceId, pickImage()) + .then((value) { + setState(() => sourceAdded = true); + }); + }, + child: const Text('Add source (asset image)'), + ), + TextButton( + onPressed: sourceAdded + ? () async { + await removeLayer(layerId); + removeImageSource(sourceId).then((value) { + setState(() => sourceAdded = false); + }); + } + : null, + child: const Text('Remove source (asset image)'), + ), + TextButton( + onPressed: + sourceAdded ? () => addLayer(layerId, sourceId) : null, + child: const Text('Show layer'), + ), + TextButton( + onPressed: sourceAdded + ? () => addLayerBelow(layerId, sourceId, 'water') + : null, + child: const Text('Show layer below water'), + ), + TextButton( + onPressed: sourceAdded ? () => removeLayer(layerId) : null, + child: const Text('Hide layer'), + ), + TextButton( + onPressed: sourceAdded + ? () async { + setState(() => imageFlag = !imageFlag); + updateImageSourceFromAsset(sourceId, pickImage()); + } + : null, + child: const Text('Change image'), + ), + ], + ), + Expanded( + child: MaplibreMap( + onMapCreated: _onMapCreated, + initialCameraPosition: const CameraPosition( + target: LatLng(-33.852, 151.211), + zoom: 11.0, + ), + ), + ), + ], + ), + ); + } +} diff --git a/example/lib/place_symbol.dart b/example/lib/annotation_symbol_page.dart similarity index 54% rename from example/lib/place_symbol.dart rename to example/lib/annotation_symbol_page.dart index 1f4161a73..688ca2d86 100644 --- a/example/lib/place_symbol.dart +++ b/example/lib/annotation_symbol_page.dart @@ -10,28 +10,18 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; -import 'page.dart'; - -class PlaceSymbolPage extends ExamplePage { - const PlaceSymbolPage({super.key}) - : super(const Icon(Icons.place), 'Place symbol'); - - @override - Widget build(BuildContext context) { - return const PlaceSymbolBody(); - } -} - -class PlaceSymbolBody extends StatefulWidget { - const PlaceSymbolBody({super.key}); +class AnnotationSymbolPage extends StatefulWidget { + const AnnotationSymbolPage({super.key}); @override - State createState() => PlaceSymbolBodyState(); + State createState() => _AnnotationSymbolPageState(); } -class PlaceSymbolBodyState extends State { - PlaceSymbolBodyState(); +class _AnnotationSymbolPageState extends State { + _AnnotationSymbolPageState(); static const LatLng center = LatLng(-33.86711, 151.1947171); @@ -266,14 +256,107 @@ class PlaceSymbolBodyState extends State { @override Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Center( - child: SizedBox( - width: 300.0, - height: 200.0, + return ExampleScaffold( + page: ExamplePage.annotationSymbol, + body: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Wrap( + alignment: WrapAlignment.center, + children: [ + TextButton( + child: const Text('add'), + onPressed: () => + (_symbolCount == 12) ? null : _add("custom-marker"), + ), + TextButton( + child: const Text('add all'), + onPressed: () => + (_symbolCount == 12) ? null : _addAll("custom-marker"), + ), + TextButton( + child: const Text('add (custom icon)'), + onPressed: () => (_symbolCount == 12) + ? null + : _add("assets/symbols/custom-icon.png"), + ), + TextButton( + onPressed: (_selectedSymbol == null) ? null : _remove, + child: const Text('remove'), + ), + TextButton( + onPressed: _changeIconOverlap, + child: Text( + '${_iconAllowOverlap ? 'disable' : 'enable'} icon overlap'), + ), + TextButton( + onPressed: (_symbolCount == 0) ? null : _removeAll, + child: const Text('remove all'), + ), + TextButton( + child: const Text('add (asset image)'), + onPressed: () => (_symbolCount == 12) + ? null + : _add( + "assetImage"), //assetImage added to the style in _onStyleLoaded + ), + TextButton( + child: const Text('add (network image)'), + onPressed: () => (_symbolCount == 12) + ? null + : _add( + "networkImage"), //networkImage added to the style in _onStyleLoaded + ), + TextButton( + child: const Text('add (custom font)'), + onPressed: () => + (_symbolCount == 12) ? null : _add("customFont"), + ) + ], + ), + Wrap( + alignment: WrapAlignment.center, + children: [ + TextButton( + onPressed: (_selectedSymbol == null) ? null : _changeAlpha, + child: const Text('change alpha'), + ), + TextButton( + onPressed: (_selectedSymbol == null) ? null : _changeIconOffset, + child: const Text('change icon offset'), + ), + TextButton( + onPressed: (_selectedSymbol == null) ? null : _changeIconAnchor, + child: const Text('change icon anchor'), + ), + TextButton( + onPressed: (_selectedSymbol == null) ? null : _toggleDraggable, + child: const Text('toggle draggable'), + ), + TextButton( + onPressed: (_selectedSymbol == null) ? null : _changePosition, + child: const Text('change position'), + ), + TextButton( + onPressed: (_selectedSymbol == null) ? null : _changeRotation, + child: const Text('change rotation'), + ), + TextButton( + onPressed: (_selectedSymbol == null) ? null : _toggleVisible, + child: const Text('toggle visible'), + ), + TextButton( + onPressed: (_selectedSymbol == null) ? null : _changeZIndex, + child: const Text('change zIndex'), + ), + TextButton( + onPressed: (_selectedSymbol == null) ? null : _getLatLng, + child: const Text('get current LatLng'), + ), + ], + ), + Expanded( child: MaplibreMap( onMapCreated: _onMapCreated, onStyleLoadedCallback: _onStyleLoaded, @@ -283,129 +366,8 @@ class PlaceSymbolBodyState extends State { ), ), ), - ), - Expanded( - child: SingleChildScrollView( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Row( - children: [ - Column( - children: [ - TextButton( - child: const Text('add'), - onPressed: () => (_symbolCount == 12) - ? null - : _add("custom-marker"), - ), - TextButton( - child: const Text('add all'), - onPressed: () => (_symbolCount == 12) - ? null - : _addAll("custom-marker"), - ), - TextButton( - child: const Text('add (custom icon)'), - onPressed: () => (_symbolCount == 12) - ? null - : _add("assets/symbols/custom-icon.png"), - ), - TextButton( - onPressed: (_selectedSymbol == null) ? null : _remove, - child: const Text('remove'), - ), - TextButton( - onPressed: _changeIconOverlap, - child: Text( - '${_iconAllowOverlap ? 'disable' : 'enable'} icon overlap'), - ), - TextButton( - onPressed: (_symbolCount == 0) ? null : _removeAll, - child: const Text('remove all'), - ), - TextButton( - child: const Text('add (asset image)'), - onPressed: () => (_symbolCount == 12) - ? null - : _add( - "assetImage"), //assetImage added to the style in _onStyleLoaded - ), - TextButton( - child: const Text('add (network image)'), - onPressed: () => (_symbolCount == 12) - ? null - : _add( - "networkImage"), //networkImage added to the style in _onStyleLoaded - ), - TextButton( - child: const Text('add (custom font)'), - onPressed: () => - (_symbolCount == 12) ? null : _add("customFont"), - ) - ], - ), - Column( - children: [ - TextButton( - onPressed: - (_selectedSymbol == null) ? null : _changeAlpha, - child: const Text('change alpha'), - ), - TextButton( - onPressed: (_selectedSymbol == null) - ? null - : _changeIconOffset, - child: const Text('change icon offset'), - ), - TextButton( - onPressed: (_selectedSymbol == null) - ? null - : _changeIconAnchor, - child: const Text('change icon anchor'), - ), - TextButton( - onPressed: (_selectedSymbol == null) - ? null - : _toggleDraggable, - child: const Text('toggle draggable'), - ), - TextButton( - onPressed: (_selectedSymbol == null) - ? null - : _changePosition, - child: const Text('change position'), - ), - TextButton( - onPressed: (_selectedSymbol == null) - ? null - : _changeRotation, - child: const Text('change rotation'), - ), - TextButton( - onPressed: - (_selectedSymbol == null) ? null : _toggleVisible, - child: const Text('toggle visible'), - ), - TextButton( - onPressed: - (_selectedSymbol == null) ? null : _changeZIndex, - child: const Text('change zIndex'), - ), - TextButton( - onPressed: - (_selectedSymbol == null) ? null : _getLatLng, - child: const Text('get current LatLng'), - ), - ], - ), - ], - ) - ], - ), - ), - ), - ], + ], + ), ); } } diff --git a/example/lib/attribution.dart b/example/lib/attribution_page.dart similarity index 55% rename from example/lib/attribution.dart rename to example/lib/attribution_page.dart index 8b17a869a..5466fd3a3 100644 --- a/example/lib/attribution.dart +++ b/example/lib/attribution_page.dart @@ -1,51 +1,50 @@ import 'package:flutter/material.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; -import 'page.dart'; - -class AttributionPage extends ExamplePage { - const AttributionPage({super.key}) - : super(const Icon(Icons.thumb_up), 'Attribution'); - - @override - Widget build(BuildContext context) { - return const AttributionBody(); - } -} - -class AttributionBody extends StatefulWidget { - const AttributionBody({super.key}); +class AttributionPage extends StatefulWidget { + const AttributionPage({super.key}); @override - State createState() => _AttributionBodyState(); + State createState() => _AttributionPageState(); } -class _AttributionBodyState extends State { +class _AttributionPageState extends State { AttributionButtonPosition? attributionButtonPosition; bool useDefaultAttributionPosition = true; @override Widget build(BuildContext context) { - return Column( - children: [ - const Text("Set attribution position"), - Wrap( - children: [ - buildDefaultPositionButton(), - buildPositionButton(null), - buildPositionButton(AttributionButtonPosition.topRight), - buildPositionButton(AttributionButtonPosition.topLeft), - buildPositionButton(AttributionButtonPosition.bottomRight), - buildPositionButton(AttributionButtonPosition.bottomLeft), - ], - ), - Expanded( - child: buildMap( - attributionButtonPosition, - useDefaultAttributionPosition, + return ExampleScaffold( + page: ExamplePage.attribution, + body: Column( + children: [ + const Text("Set attribution position"), + Padding( + padding: const EdgeInsets.all(8), + child: Wrap( + alignment: WrapAlignment.center, + spacing: 8, + runSpacing: 8, + children: [ + buildDefaultPositionButton(), + buildPositionButton(null), + buildPositionButton(AttributionButtonPosition.topRight), + buildPositionButton(AttributionButtonPosition.topLeft), + buildPositionButton(AttributionButtonPosition.bottomRight), + buildPositionButton(AttributionButtonPosition.bottomLeft), + ], + ), ), - ), - ], + Expanded( + child: buildMap( + attributionButtonPosition, + useDefaultAttributionPosition, + ), + ), + ], + ), ); } diff --git a/example/lib/place_batch.dart b/example/lib/batch_operation_page.dart similarity index 72% rename from example/lib/place_batch.dart rename to example/lib/batch_operation_page.dart index e85fb23d2..c704c07b6 100644 --- a/example/lib/place_batch.dart +++ b/example/lib/batch_operation_page.dart @@ -4,9 +4,10 @@ import 'package:flutter/material.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; -import 'page.dart'; -import 'util.dart'; +import 'common/util.dart'; const fillOptions = [ FillOptions( @@ -44,33 +45,19 @@ const fillOptions = [ ], fillColor: "#FF0000"), ]; -class BatchAddPage extends ExamplePage { - const BatchAddPage({super.key}) - : super(const Icon(Icons.check_circle), 'Batch add/remove'); +class BatchOperationPage extends StatefulWidget { + const BatchOperationPage({super.key}); @override - Widget build(BuildContext context) { - return const BatchAddBody(); - } + State createState() => _BatchOperationPageState(); } -class BatchAddBody extends StatefulWidget { - const BatchAddBody({super.key}); - - @override - State createState() => BatchAddBodyState(); -} - -class BatchAddBodyState extends State { - BatchAddBodyState(); - +class _BatchOperationPageState extends State { List _fills = []; List _circles = []; List _lines = []; List _symbols = []; - static const LatLng center = LatLng(-33.86711, 151.1947171); - late MaplibreMapController controller; void _onMapCreated(MaplibreMapController controller) { @@ -141,13 +128,20 @@ class BatchAddBodyState extends State { @override Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Center( - child: SizedBox( - height: 200.0, + return ExampleScaffold( + page: ExamplePage.batchOperation, + body: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Wrap( + alignment: WrapAlignment.center, + children: [ + TextButton(onPressed: _add, child: const Text('batch add')), + TextButton(onPressed: _remove, child: const Text('batch remove')), + ], + ), + Expanded( child: MaplibreMap( onMapCreated: _onMapCreated, onStyleLoadedCallback: () => addImageFromAsset(controller, @@ -164,30 +158,8 @@ class BatchAddBodyState extends State { ], ), ), - ), - Expanded( - child: SingleChildScrollView( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Row( - children: [ - Column( - children: [ - TextButton( - onPressed: _add, child: const Text('batch add')), - TextButton( - onPressed: _remove, - child: const Text('batch remove')), - ], - ), - ], - ) - ], - ), - ), - ), - ], + ], + ), ); } } diff --git a/example/lib/click_annotations.dart b/example/lib/click_annotations_page.dart similarity index 79% rename from example/lib/click_annotations.dart rename to example/lib/click_annotations_page.dart index 409254a88..36c80174b 100644 --- a/example/lib/click_annotations.dart +++ b/example/lib/click_annotations_page.dart @@ -4,29 +4,20 @@ import 'package:flutter/material.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; -import 'page.dart'; -import 'util.dart'; +import 'common/util.dart'; -class ClickAnnotationPage extends ExamplePage { - const ClickAnnotationPage({super.key}) - : super(const Icon(Icons.check_circle), 'Annotation tap'); +class ClickAnnotationPage extends StatefulWidget { + const ClickAnnotationPage({super.key}); @override - Widget build(BuildContext context) { - return const ClickAnnotationBody(); - } -} - -class ClickAnnotationBody extends StatefulWidget { - const ClickAnnotationBody({super.key}); - - @override - State createState() => ClickAnnotationBodyState(); + State createState() => _ClickAnnotationPageState(); } -class ClickAnnotationBodyState extends State { - ClickAnnotationBodyState(); +class _ClickAnnotationPageState extends State { + _ClickAnnotationPageState(); static const LatLng center = LatLng(-33.88, 151.16); @@ -52,7 +43,7 @@ class ClickAnnotationBodyState extends State { _showSnackBar(String type, String id) { final snackBar = SnackBar( content: Text( - 'Tapped $type $id', + 'Tapped $type with id "$id"', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, @@ -139,18 +130,21 @@ class ClickAnnotationBodyState extends State { @override Widget build(BuildContext context) { - return MaplibreMap( - annotationOrder: const [ - AnnotationType.fill, - AnnotationType.line, - AnnotationType.circle, - AnnotationType.symbol, - ], - onMapCreated: _onMapCreated, - onStyleLoadedCallback: _onStyleLoaded, - initialCameraPosition: const CameraPosition( - target: center, - zoom: 12.0, + return ExampleScaffold( + page: ExamplePage.clickAnnotation, + body: MaplibreMap( + annotationOrder: const [ + AnnotationType.fill, + AnnotationType.line, + AnnotationType.circle, + AnnotationType.symbol, + ], + onMapCreated: _onMapCreated, + onStyleLoadedCallback: _onStyleLoaded, + initialCameraPosition: const CameraPosition( + target: center, + zoom: 12.0, + ), ), ); } diff --git a/example/lib/common/example_scaffold.dart b/example/lib/common/example_scaffold.dart new file mode 100644 index 000000000..c97d77a43 --- /dev/null +++ b/example/lib/common/example_scaffold.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:maplibre_gl_example/main.dart'; + +class ExampleScaffold extends StatelessWidget { + final ExamplePage page; + final Widget body; + final Widget? floatingActionButton; + + const ExampleScaffold({ + super.key, + required this.page, + required this.body, + this.floatingActionButton, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text(page.title)), + floatingActionButton: floatingActionButton, + body: body, + ); + } +} diff --git a/example/lib/util.dart b/example/lib/common/util.dart similarity index 100% rename from example/lib/util.dart rename to example/lib/common/util.dart diff --git a/example/lib/custom_marker.dart b/example/lib/custom_marker_page.dart similarity index 90% rename from example/lib/custom_marker.dart rename to example/lib/custom_marker_page.dart index c4ef10917..ca48724b9 100644 --- a/example/lib/custom_marker.dart +++ b/example/lib/custom_marker_page.dart @@ -5,29 +5,18 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; // ignore: unnecessary_import import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; +import 'package:maplibre_gl_example/main.dart'; -import 'page.dart'; - -const randomMarkerNum = 100; - -class CustomMarkerPage extends ExamplePage { - const CustomMarkerPage({super.key}) - : super(const Icon(Icons.place), 'Custom marker'); - - @override - Widget build(BuildContext context) { - return const CustomMarker(); - } -} - -class CustomMarker extends StatefulWidget { - const CustomMarker({super.key}); +class CustomMarkerPage extends StatefulWidget { + const CustomMarkerPage({super.key}); @override - State createState() => CustomMarkerState(); + State createState() => _CustomMarkerPageState(); } -class CustomMarkerState extends State { +class _CustomMarkerPageState extends State { + static const randomMarkerNum = 100; final _rnd = Random(); late MaplibreMapController _mapController; @@ -60,11 +49,9 @@ class CustomMarkerState extends State { } void _updateMarkerPosition() { - final coordinates = []; - - for (final markerState in _markerStates) { - coordinates.add(markerState.getCoordinate()); - } + final coordinates = _markerStates + .map((markerState) => markerState.getCoordinate()) + .toList(growable: false); _mapController.toScreenLocationBatch(coordinates).then((points) { _markerStates.asMap().forEach((i, value) { @@ -88,7 +75,8 @@ class CustomMarkerState extends State { @override Widget build(BuildContext context) { - return Scaffold( + return ExampleScaffold( + page: ExamplePage.customMarker, body: Stack(children: [ MaplibreMap( trackCameraPosition: true, diff --git a/example/lib/full_map.dart b/example/lib/full_map.dart deleted file mode 100644 index f852efc4a..000000000 --- a/example/lib/full_map.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:maplibre_gl/maplibre_gl.dart'; - -import 'page.dart'; - -class FullMapPage extends ExamplePage { - const FullMapPage({super.key}) - : super(const Icon(Icons.map), 'Full screen map'); - - @override - Widget build(BuildContext context) { - return const FullMap(); - } -} - -class FullMap extends StatefulWidget { - const FullMap({super.key}); - - @override - State createState() => FullMapState(); -} - -class FullMapState extends State { - MaplibreMapController? mapController; - var isLight = true; - - _onMapCreated(MaplibreMapController controller) { - mapController = controller; - } - - _onStyleLoadedCallback() { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: const Text("Style loaded :)"), - backgroundColor: Theme.of(context).primaryColor, - duration: const Duration(seconds: 1), - ), - ); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - // TODO: commented out when cherry-picking https://github.com/flutter-mapbox-gl/maps/pull/775 - // needs different dark and light styles in this repo - // floatingActionButton: Padding( - // padding: const EdgeInsets.all(32.0), - // child: FloatingActionButton( - // child: Icon(Icons.swap_horiz), - // onPressed: () => setState( - // () => isLight = !isLight, - // ), - // ), - // ), - body: MaplibreMap( - onMapCreated: _onMapCreated, - initialCameraPosition: const CameraPosition(target: LatLng(0.0, 0.0)), - onStyleLoadedCallback: _onStyleLoadedCallback, - )); - } -} diff --git a/example/lib/fullscreen_map_page.dart b/example/lib/fullscreen_map_page.dart new file mode 100644 index 000000000..a7f1e84dc --- /dev/null +++ b/example/lib/fullscreen_map_page.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; + +class FullscreenMapPage extends StatefulWidget { + const FullscreenMapPage({super.key}); + + @override + State createState() => _FullscreenMapPageState(); +} + +class _FullscreenMapPageState extends State { + MaplibreMapController? mapController; + var isLight = true; + + _onMapCreated(MaplibreMapController controller) { + mapController = controller; + } + + _onStyleLoadedCallback() { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: const Text("Style loaded :)"), + backgroundColor: Theme.of(context).primaryColor, + duration: const Duration(seconds: 1), + ), + ); + } + + @override + Widget build(BuildContext context) { + return ExampleScaffold( + page: ExamplePage.fullscreen, + // TODO: commented out when cherry-picking https://github.com/flutter-mapbox-gl/maps/pull/775 + // needs different dark and light styles in this repo + // floatingActionButton: Padding( + // padding: const EdgeInsets.all(32.0), + // child: FloatingActionButton( + // child: Icon(Icons.swap_horiz), + // onPressed: () => setState( + // () => isLight = !isLight, + // ), + // ), + // ), + body: MaplibreMap( + onMapCreated: _onMapCreated, + initialCameraPosition: const CameraPosition(target: LatLng(0.0, 0.0)), + onStyleLoadedCallback: _onStyleLoadedCallback, + ), + ); + } +} diff --git a/example/lib/get_map_informations.dart b/example/lib/get_map_informations.dart deleted file mode 100644 index 2437a30d6..000000000 --- a/example/lib/get_map_informations.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:maplibre_gl/maplibre_gl.dart'; - -import 'page.dart'; - -class GetMapInfoPage extends ExamplePage { - const GetMapInfoPage({super.key}) - : super(const Icon(Icons.info), 'Get map state'); - - @override - Widget build(BuildContext context) { - return const GetMapInfoBody(); - } -} - -class GetMapInfoBody extends StatefulWidget { - const GetMapInfoBody({super.key}); - - @override - State createState() => _GetMapInfoBodyState(); -} - -class _GetMapInfoBodyState extends State { - MaplibreMapController? controller; - String data = ''; - - void onMapCreated(MaplibreMapController controller) { - setState(() { - this.controller = controller; - }); - } - - void displaySources() async { - if (controller == null) { - return; - } - List sources = await controller!.getSourceIds(); - setState(() { - data = 'Sources: ${sources.map((e) => '"$e"').join(', ')}'; - }); - } - - void displayLayers() async { - if (controller == null) { - return; - } - List layers = (await controller!.getLayerIds()).cast(); - setState(() { - data = 'Layers: ${layers.map((e) => '"$e"').join(', ')}'; - }); - } - - @override - Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Center( - child: SizedBox( - width: 300.0, - height: 200.0, - child: MaplibreMap( - initialCameraPosition: const CameraPosition( - target: LatLng(-33.852, 151.211), - zoom: 11.0, - ), - onMapCreated: onMapCreated, - compassEnabled: false, - annotationOrder: const [], - myLocationEnabled: false, - styleString: '''{ - "version": 8, - "sources": { - "OSM": { - "type": "raster", - "tiles": [ - "https://a.tile.openstreetmap.org/{z}/{x}/{y}.png", - "https://b.tile.openstreetmap.org/{z}/{x}/{y}.png", - "https://c.tile.openstreetmap.org/{z}/{x}/{y}.png" - ], - "tileSize": 256, - "attribution": "© OpenStreetMap contributors", - "maxzoom": 18 - } - }, - "layers": [ - { - "id": "OSM-layer", - "source": "OSM", - "type": "raster" - } - ] - }''', - ), - ), - ), - const Center(child: Text('© OpenStreetMap contributors')), - Expanded( - child: SingleChildScrollView( - child: Column( - children: [ - const SizedBox(height: 30), - Center(child: Text(data)), - const SizedBox(height: 30), - ElevatedButton( - onPressed: controller == null ? null : displayLayers, - child: const Text('Get map layers'), - ), - ElevatedButton( - onPressed: controller == null ? null : displaySources, - child: const Text('Get map sources'), - ) - ], - ), - )), - ], - ); - } -} diff --git a/example/lib/given_bounds.dart b/example/lib/given_bounds.dart deleted file mode 100644 index 35e1c0226..000000000 --- a/example/lib/given_bounds.dart +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:maplibre_gl/maplibre_gl.dart'; - -import 'page.dart'; - -class GivenBoundsPage extends ExamplePage { - const GivenBoundsPage({super.key}) - : super(const Icon(Icons.map_sharp), 'Changing given bounds'); - - @override - Widget build(BuildContext context) { - return const GivenBounds(); - } -} - -class GivenBounds extends StatefulWidget { - const GivenBounds({super.key}); - - @override - State createState() => GivenBoundsState(); -} - -class GivenBoundsState extends State { - late MaplibreMapController mapController; - - void _onMapCreated(MaplibreMapController controller) { - mapController = controller; - } - - @override - Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Center( - child: SizedBox( - width: 300.0, - height: 200.0, - child: MaplibreMap( - onMapCreated: _onMapCreated, - initialCameraPosition: - const CameraPosition(target: LatLng(0.0, 0.0)), - ), - ), - ), - TextButton( - onPressed: () async { - await mapController.setCameraBounds( - west: 5.98865807458, - south: 47.3024876979, - east: 15.0169958839, - north: 54.983104153, - padding: 25, - ); - }, - child: const Text('Set bounds to Germany'), - ), - TextButton( - onPressed: () async { - await mapController.setCameraBounds( - west: -18, - south: -40, - east: 54, - north: 40, - padding: 25, - ); - }, - child: const Text('Set bounds to Africa'), - ), - ], - ); - } -} diff --git a/example/lib/local_style.dart b/example/lib/local_style.dart deleted file mode 100644 index 5d49778c6..000000000 --- a/example/lib/local_style.dart +++ /dev/null @@ -1,76 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:maplibre_gl/maplibre_gl.dart'; -import 'package:path_provider/path_provider.dart'; - -import 'page.dart'; - -class LocalStylePage extends ExamplePage { - const LocalStylePage({super.key}) - : super(const Icon(Icons.map), 'Local style'); - - @override - Widget build(BuildContext context) { - return const LocalStyle(); - } -} - -class LocalStyle extends StatefulWidget { - const LocalStyle({super.key}); - - @override - State createState() => LocalStyleState(); -} - -class LocalStyleState extends State { - MaplibreMapController? mapController; - String? styleAbsoluteFilePath; - - @override - initState() { - super.initState(); - - getApplicationDocumentsDirectory().then((dir) async { - String documentDir = dir.path; - String stylesDir = '$documentDir/styles'; - String styleJSON = - '{"version":8,"name":"Demo style","center":[50,10],"zoom":4,"sources":{"demotiles":{"type":"vector","url":"https://demotiles.maplibre.org/tiles/tiles.json"}},"sprite":"","glyphs":"https://orangemug.github.io/font-glyphs/glyphs/{fontstack}/{range}.pbf","layers":[{"id":"background","type":"background","paint":{"background-color":"rgba(255, 255, 255, 1)"}},{"id":"countries","type":"line","source":"demotiles","source-layer":"countries","paint":{"line-color":"rgba(0, 0, 0, 1)","line-width":1,"line-opacity":1}}]}'; - - await Directory(stylesDir).create(recursive: true); - - File styleFile = File('$stylesDir/style.json'); - - await styleFile.writeAsString(styleJSON); - - setState(() { - styleAbsoluteFilePath = styleFile.path; - }); - }); - } - - void _onMapCreated(MaplibreMapController controller) { - mapController = controller; - } - - @override - Widget build(BuildContext context) { - final styleAbsoluteFilePath = this.styleAbsoluteFilePath; - - if (styleAbsoluteFilePath == null) { - return const Scaffold( - body: Center(child: Text('Creating local style file...')), - ); - } - - return Scaffold( - body: MaplibreMap( - styleString: styleAbsoluteFilePath, - onMapCreated: _onMapCreated, - initialCameraPosition: const CameraPosition(target: LatLng(0.0, 0.0)), - onStyleLoadedCallback: onStyleLoadedCallback, - )); - } - - void onStyleLoadedCallback() {} -} diff --git a/example/lib/local_style_page.dart b/example/lib/local_style_page.dart new file mode 100644 index 000000000..9aacdb092 --- /dev/null +++ b/example/lib/local_style_page.dart @@ -0,0 +1,68 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:path_provider/path_provider.dart'; + +class LocalStylePage extends StatefulWidget { + const LocalStylePage({super.key}); + + @override + State createState() => _LocalStylePageState(); +} + +class _LocalStylePageState extends State { + MaplibreMapController? mapController; + String? styleAbsoluteFilePath; + + @override + initState() { + super.initState(); + + getApplicationDocumentsDirectory().then((dir) async { + String documentDir = dir.path; + String stylesDir = '$documentDir/styles'; + String styleJSON = MaplibreStyles.demo; + + await Directory(stylesDir).create(recursive: true); + + File styleFile = File('$stylesDir/style.json'); + + await styleFile.writeAsString(styleJSON); + + setState(() { + styleAbsoluteFilePath = styleFile.path; + }); + }); + } + + void _onMapCreated(MaplibreMapController controller) { + mapController = controller; + } + + @override + Widget build(BuildContext context) { + final styleAbsoluteFilePath = this.styleAbsoluteFilePath; + + if (styleAbsoluteFilePath == null) { + return const ExampleScaffold( + page: ExamplePage.localStyle, + body: Center(child: Text('Creating local style file...')), + ); + } + + return ExampleScaffold( + page: ExamplePage.localStyle, + body: MaplibreMap( + styleString: styleAbsoluteFilePath, + onMapCreated: _onMapCreated, + initialCameraPosition: const CameraPosition(target: LatLng(0.0, 0.0)), + onStyleLoadedCallback: onStyleLoadedCallback, + ), + ); + } + + void onStyleLoadedCallback() {} +} diff --git a/example/lib/localized_map.dart b/example/lib/localized_map_page.dart similarity index 77% rename from example/lib/localized_map.dart rename to example/lib/localized_map_page.dart index 40eb49e00..6e426dd67 100644 --- a/example/lib/localized_map.dart +++ b/example/lib/localized_map_page.dart @@ -2,34 +2,25 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; -import 'page.dart'; - -class LocalizedMapPage extends ExamplePage { - const LocalizedMapPage({super.key}) - : super(const Icon(Icons.map), 'Localized screen map'); - - @override - Widget build(BuildContext context) { - return const LocalizedMap(); - } -} - -class LocalizedMap extends StatefulWidget { - const LocalizedMap({super.key}); +class LocalizedMapPage extends StatefulWidget { + const LocalizedMapPage({super.key}); @override - State createState() => LocalizedMapState(); + State createState() => _LocalizedMapPageState(); } -class LocalizedMapState extends State { +class _LocalizedMapPageState extends State { final _mapReadyCompleter = Completer(); var _mapLanguage = "en"; @override Widget build(BuildContext context) { - return Scaffold( + return ExampleScaffold( + page: ExamplePage.localized, body: Column( children: [ DropdownButton( diff --git a/example/lib/main.dart b/example/lib/main.dart index 586ca4ca7..95bdc2e29 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,128 +1,108 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:io'; - -import 'package:device_info_plus/device_info_plus.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:location/location.dart'; -import 'package:maplibre_gl/maplibre_gl.dart'; -import 'package:maplibre_gl_example/attribution.dart'; -import 'package:maplibre_gl_example/get_map_informations.dart'; -import 'package:maplibre_gl_example/given_bounds.dart'; -import 'package:maplibre_gl_example/localized_map.dart'; +import 'package:flutter_web_plugins/url_strategy.dart'; +import 'package:maplibre_gl_example/annotation_circle_page.dart'; +import 'package:maplibre_gl_example/annotation_fill_page.dart'; +import 'package:maplibre_gl_example/annotation_layer_page.dart'; +import 'package:maplibre_gl_example/annotation_line_page.dart'; +import 'package:maplibre_gl_example/annotation_order_page.dart'; +import 'package:maplibre_gl_example/annotation_source_page.dart'; +import 'package:maplibre_gl_example/annotation_symbol_page.dart'; +import 'package:maplibre_gl_example/attribution_page.dart'; +import 'package:maplibre_gl_example/batch_operation_page.dart'; +import 'package:maplibre_gl_example/click_annotations_page.dart'; +import 'package:maplibre_gl_example/custom_marker_page.dart'; +import 'package:maplibre_gl_example/fullscreen_map_page.dart'; +import 'package:maplibre_gl_example/local_style_page.dart'; +import 'package:maplibre_gl_example/localized_map_page.dart'; +import 'package:maplibre_gl_example/main_page.dart'; +import 'package:maplibre_gl_example/map_state_page.dart'; +import 'package:maplibre_gl_example/move_camera_animated.dart'; +import 'package:maplibre_gl_example/move_camera_page.dart'; import 'package:maplibre_gl_example/no_location_permission_page.dart'; +import 'package:maplibre_gl_example/offline_regions_page.dart'; +import 'package:maplibre_gl_example/scrolling_map_page.dart'; +import 'package:maplibre_gl_example/set_map_bounds_page.dart'; +import 'package:maplibre_gl_example/user_interface_page.dart'; +import 'package:maplibre_gl_example/various_sources_page.dart'; -import 'animate_camera.dart'; -import 'annotation_order_maps.dart'; -import 'click_annotations.dart'; -import 'custom_marker.dart'; -import 'full_map.dart'; -import 'layer.dart'; -import 'line.dart'; -import 'local_style.dart'; -import 'map_ui.dart'; -import 'move_camera.dart'; -import 'offline_regions.dart'; -import 'page.dart'; -import 'place_batch.dart'; -import 'place_circle.dart'; -import 'place_fill.dart'; -import 'place_source.dart'; -import 'place_symbol.dart'; -import 'scrolling_map.dart'; -import 'sources.dart'; +final routes = { + ExamplePage.main.path: (context) => const MainPage(), + ExamplePage.userInterface.path: (context) => const UserInterfacePage(), + ExamplePage.fullscreen.path: (context) => const FullscreenMapPage(), + ExamplePage.localized.path: (context) => const LocalizedMapPage(), + ExamplePage.moveCameraAnimated.path: (context) => const AnimateCameraPage(), + ExamplePage.moveCamera.path: (context) => const MoveCameraPage(), + ExamplePage.localStyle.path: (context) => const LocalStylePage(), + ExamplePage.scrolling.path: (context) => const ScrollingMapPage(), + ExamplePage.offlineRegions.path: (context) => const OfflineRegionsPage(), + ExamplePage.setMapBounds.path: (context) => const SetMapBoundsPage(), + ExamplePage.mapState.path: (context) => const MapStatePage(), + ExamplePage.noLocationPermission.path: (context) => + const NoLocationPermissionPage(), + ExamplePage.annotationSymbol.path: (context) => const AnnotationSymbolPage(), + ExamplePage.annotationSource.path: (context) => const AnnotationSourcePage(), + ExamplePage.annotationLine.path: (context) => const AnnotationLinePage(), + ExamplePage.annotationLayer.path: (context) => const AnnotationLayerPage(), + ExamplePage.annotationCircle.path: (context) => const AnnotationCirclePage(), + ExamplePage.annotationFill.path: (context) => const AnnotationFillPage(), + ExamplePage.batchOperation.path: (context) => const BatchOperationPage(), + ExamplePage.annotationOrder.path: (context) => const AnnotationOrderPage(), + ExamplePage.customMarker.path: (context) => const CustomMarkerPage(), + ExamplePage.clickAnnotation.path: (context) => const ClickAnnotationPage(), + ExamplePage.variousSources.path: (context) => const VariousSourcesPage(), + ExamplePage.attribution.path: (context) => const AttributionPage(), +}; -final List _allPages = [ - const MapUiPage(), - const FullMapPage(), - const LocalizedMapPage(), - const AnimateCameraPage(), - const MoveCameraPage(), - const PlaceSymbolPage(), - const PlaceSourcePage(), - const LinePage(), - const LocalStylePage(), - const LayerPage(), - const PlaceCirclePage(), - const PlaceFillPage(), - const ScrollingMapPage(), - const OfflineRegionsPage(), - const AnnotationOrderPage(), - const CustomMarkerPage(), - const BatchAddPage(), - const ClickAnnotationPage(), - const Sources(), - const GivenBoundsPage(), - const GetMapInfoPage(), - const NoLocationPermissionPage(), - const AttributionPage(), -]; +enum ExamplePage { + main('/', 'MapLibre Examples'), + userInterface('/user-interface', 'User interface'), + fullscreen('/fullscreen', 'Fullscreen map'), + localized('/localized', 'Localized map'), + moveCameraAnimated('/move-camera-animated', 'Move camera animated'), + moveCamera('/move-camera', 'Move camera'), + localStyle('/local-style', 'Local style'), + scrolling('/scrolling', 'Scrolling map'), + offlineRegions('/offline-regions', 'Offline regions'), + setMapBounds('/set-map-bounds', 'Set map bounds'), + mapState('/map-info', 'Get map state'), + noLocationPermission('/no-location-permission', 'No user location permission', + needsLocationPermission: false), + annotationSymbol('/annotation-symbol', 'Symbol'), + annotationSource('/annotation-source', 'Source'), + annotationLine('/annotation-line', 'Line'), + annotationLayer('/annotation-layer', 'Layer'), + annotationCircle('/annotation-circle', 'Circle'), + annotationFill('/annotation-fill', 'Fill'), + batchOperation('/batch-operation', 'Batch operation'), + annotationOrder('/annotation-order', 'Annotation order'), + customMarker('/custom-marker', 'Custom marker'), + clickAnnotation('/click-annotation', 'Click annotation'), + variousSources('/various-sources', 'Various sources'), + attribution('/attribution', 'Attribution'); -class MapsDemo extends StatefulWidget { - const MapsDemo({super.key}); + const ExamplePage( + this.path, + this.title, { + this.needsLocationPermission = true, + }); - @override - State createState() => _MapsDemoState(); -} - -class _MapsDemoState extends State { - /// Determine the android version of the phone and turn off HybridComposition - /// on older sdk versions to improve performance for these - /// - /// !!! Hybrid composition is currently broken do no use !!! - Future initHybridComposition() async { - if (!kIsWeb && Platform.isAndroid) { - final androidInfo = await DeviceInfoPlugin().androidInfo; - final sdkVersion = androidInfo.version.sdkInt; - if (sdkVersion >= 29) { - MaplibreMap.useHybridComposition = true; - } else { - MaplibreMap.useHybridComposition = false; - } - } - } - - void _pushPage(BuildContext context, ExamplePage page) async { - if (!kIsWeb && page.needsLocationPermission) { - final location = Location(); - final hasPermissions = await location.hasPermission(); - if (hasPermissions != PermissionStatus.granted) { - await location.requestPermission(); - } - } - if (context.mounted) { - Navigator.of(context).push(MaterialPageRoute( - builder: (_) => Scaffold( - appBar: AppBar(title: Text(page.title)), - body: page, - ), - )); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: const Text('Maplibre examples')), - body: ListView.builder( - itemCount: _allPages.length + 1, - itemBuilder: (_, int index) => index == _allPages.length - ? const AboutListTile( - applicationName: "flutter-maplibre-gl example", - ) - : ListTile( - leading: _allPages[index].leading, - title: Text(_allPages[index].title), - onTap: () => _pushPage(context, _allPages[index]), - ), - ), - ); - } + final String path; + final String title; + final bool needsLocationPermission; } void main() { - runApp(const MaterialApp(home: MapsDemo())); + usePathUrlStrategy(); + final materialTheme = ThemeData( + useMaterial3: true, + colorSchemeSeed: const Color(0xff295daa), + ); + + runApp( + MaterialApp( + theme: materialTheme, + initialRoute: ExamplePage.main.path, + routes: routes, + ), + ); } diff --git a/example/lib/main_page.dart b/example/lib/main_page.dart new file mode 100644 index 000000000..c19431fb6 --- /dev/null +++ b/example/lib/main_page.dart @@ -0,0 +1,167 @@ +import 'dart:io'; + +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:location/location.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; +import 'package:url_launcher/url_launcher_string.dart'; + +const _categories = >{ + 'General': [ + CategoryItem(ExamplePage.fullscreen, Icons.fullscreen), + CategoryItem(ExamplePage.localized, Icons.language), + CategoryItem(ExamplePage.localStyle, Icons.language), + CategoryItem(ExamplePage.scrolling, Icons.map_outlined), + CategoryItem( + ExamplePage.offlineRegions, Icons.download_for_offline_outlined), + CategoryItem(ExamplePage.mapState, Icons.info_outline), + CategoryItem(ExamplePage.noLocationPermission, Icons.gps_off), + CategoryItem(ExamplePage.variousSources, Icons.layers_outlined), + CategoryItem(ExamplePage.attribution, Icons.thumb_up_alt_outlined), + ], + 'Interactivity': [ + CategoryItem(ExamplePage.userInterface, Icons.accessibility_new_outlined), + CategoryItem(ExamplePage.moveCamera, Icons.control_camera), + CategoryItem(ExamplePage.moveCameraAnimated, Icons.animation), + CategoryItem(ExamplePage.setMapBounds, Icons.control_camera), + ], + 'Annotations': [ + CategoryItem(ExamplePage.annotationCircle, Icons.circle_outlined), + CategoryItem(ExamplePage.annotationFill, Icons.format_shapes_outlined), + CategoryItem(ExamplePage.annotationLayer, Icons.layers_outlined), + CategoryItem(ExamplePage.annotationLine, Icons.share), + CategoryItem(ExamplePage.annotationSource, Icons.source_outlined), + CategoryItem(ExamplePage.annotationSymbol, Icons.place_outlined), + CategoryItem(ExamplePage.batchOperation, Icons.list_alt), + CategoryItem(ExamplePage.annotationOrder, Icons.layers_outlined), + CategoryItem(ExamplePage.customMarker, Icons.place_outlined), + CategoryItem(ExamplePage.clickAnnotation, Icons.touch_app_outlined), + ], +}; + +class MainPage extends StatefulWidget { + const MainPage({super.key}); + + @override + State createState() => MainPageState(); +} + +class MainPageState extends State { + /// Determine the android version of the phone and turn off HybridComposition + /// on older sdk versions to improve performance for these + /// + /// upstream issue: https://github.com/flutter-mapbox-gl/maps/pull/916 + /// + /// flutter issue: https://github.com/flutter/flutter/issues/97494 + /// + /// TODO: Hybrid composition is currently broken do no use + /// https://github.com/flutter-mapbox-gl/maps/issues/1077 + Future initHybridComposition() async { + if (!kIsWeb && Platform.isAndroid) { + final androidInfo = await DeviceInfoPlugin().androidInfo; + final sdkVersion = androidInfo.version.sdkInt; + if (sdkVersion >= 29) { + MaplibreMap.useHybridComposition = true; + } else { + MaplibreMap.useHybridComposition = false; + } + } + } + + void _pushPage(BuildContext context, CategoryItem model) async { + if (!kIsWeb && model.page.needsLocationPermission) { + final location = Location(); + final hasPermissions = await location.hasPermission(); + if (hasPermissions != PermissionStatus.granted) { + final _ = await location.requestPermission(); + } + } + if (context.mounted) { + Navigator.of(context).pushNamed(model.page.path); + } + } + + @override + Widget build(BuildContext context) { + const gridDelegate = SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 300, + childAspectRatio: 3, + ); + final slivers = []; + for (final MapEntry(:key, :value) in _categories.entries) { + slivers.addAll([ + SliverToBoxAdapter(child: ListSectionTitle(key)), + SliverGrid.builder( + gridDelegate: gridDelegate, + itemCount: value.length, + itemBuilder: (_, int index) => ListTile( + leading: Icon(value[index].iconData), + title: Text(value[index].page.title), + onTap: () => _pushPage(context, value[index]), + ), + ), + ]); + } + slivers.addAll([ + const SliverToBoxAdapter(child: ListSectionTitle('About this App')), + SliverGrid( + gridDelegate: gridDelegate, + delegate: SliverChildListDelegate([ + ListTile( + title: const Text("Show licenses"), + leading: const Icon(Icons.info_outline), + onTap: () => showLicensePage( + context: context, + applicationName: 'MapLibre Examples', + ), + ), + ListTile( + title: const Text("View on pub.dev"), + leading: const Icon(Icons.flutter_dash), + onTap: () => launchUrlString( + "https://pub.dev/packages/maplibre_gl", + ), + ), + ListTile( + title: const Text("View source code"), + leading: const Icon(Icons.code), + onTap: () => launchUrlString( + "https://github.com/maplibre/flutter-maplibre-gl", + ), + ), + ]), + ), + ]); + + return ExampleScaffold( + page: ExamplePage.main, + body: CustomScrollView(slivers: slivers), + ); + } +} + +class ListSectionTitle extends StatelessWidget { + final String title; + + const ListSectionTitle(this.title, {super.key}); + + @override + Widget build(BuildContext context) { + return ListTile( + title: Text( + title, + style: TextStyle(fontSize: 18, color: Theme.of(context).primaryColor), + ), + ); + } +} + +class CategoryItem { + final ExamplePage page; + final IconData iconData; + + const CategoryItem(this.page, this.iconData); +} diff --git a/example/lib/map_state_page.dart b/example/lib/map_state_page.dart new file mode 100644 index 000000000..f869fef6b --- /dev/null +++ b/example/lib/map_state_page.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; + +class MapStatePage extends StatefulWidget { + const MapStatePage({super.key}); + + @override + State createState() => _MapStatePageState(); +} + +class _MapStatePageState extends State { + MaplibreMapController? controller; + + void onMapCreated(MaplibreMapController controller) { + setState(() { + this.controller = controller; + }); + } + + void _showSnackbar(String content) { + ScaffoldMessenger.of(context) + ..removeCurrentSnackBar() + ..showSnackBar(SnackBar(content: Text(content))); + } + + void displaySources() async { + if (controller == null) { + return; + } + List sources = await controller!.getSourceIds(); + _showSnackbar('Sources: ${sources.map((e) => '"$e"').join(', ')}'); + } + + void displayLayers() async { + if (controller == null) { + return; + } + List layers = (await controller!.getLayerIds()).cast(); + _showSnackbar('Layers: ${layers.map((e) => '"$e"').join(', ')}'); + } + + @override + Widget build(BuildContext context) { + return ExampleScaffold( + page: ExamplePage.mapState, + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8), + child: Wrap( + alignment: WrapAlignment.center, + children: [ + TextButton( + onPressed: controller == null ? null : displayLayers, + child: const Text('Get map layers'), + ), + TextButton( + onPressed: controller == null ? null : displaySources, + child: const Text('Get map sources'), + ) + ], + ), + ), + Expanded( + child: MaplibreMap( + initialCameraPosition: const CameraPosition( + target: LatLng(-33.852, 151.211), + zoom: 2, + ), + onMapCreated: onMapCreated, + annotationOrder: const [], + styleString: MaplibreStyles.demo, + ), + ), + ], + ), + ); + } +} diff --git a/example/lib/animate_camera.dart b/example/lib/move_camera_animated.dart similarity index 84% rename from example/lib/animate_camera.dart rename to example/lib/move_camera_animated.dart index ad1e14792..9b1d13417 100644 --- a/example/lib/animate_camera.dart +++ b/example/lib/move_camera_animated.dart @@ -4,27 +4,17 @@ import 'package:flutter/material.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; +import 'package:maplibre_gl_example/main.dart'; -import 'page.dart'; - -class AnimateCameraPage extends ExamplePage { - const AnimateCameraPage({super.key}) - : super(const Icon(Icons.map), 'Camera control, animated'); +class AnimateCameraPage extends StatefulWidget { + const AnimateCameraPage({super.key}); @override - Widget build(BuildContext context) { - return const AnimateCamera(); - } + State createState() => _AnimateCameraPageState(); } -class AnimateCamera extends StatefulWidget { - const AnimateCamera({super.key}); - - @override - State createState() => AnimateCameraState(); -} - -class AnimateCameraState extends State { +class _AnimateCameraPageState extends State { late MaplibreMapController mapController; void _onMapCreated(MaplibreMapController controller) { @@ -33,25 +23,16 @@ class AnimateCameraState extends State { @override Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Center( - child: SizedBox( - width: 300.0, - height: 200.0, - child: MaplibreMap( - onMapCreated: _onMapCreated, - initialCameraPosition: - const CameraPosition(target: LatLng(0.0, 0.0)), - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Column( + return ExampleScaffold( + page: ExamplePage.moveCameraAnimated, + body: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: const EdgeInsets.all(8), + child: Wrap( + alignment: WrapAlignment.center, children: [ TextButton( onPressed: () { @@ -122,10 +103,6 @@ class AnimateCameraState extends State { }, child: const Text('scrollBy'), ), - ], - ), - Column( - children: [ TextButton( onPressed: () { mapController.animateCamera( @@ -196,9 +173,17 @@ class AnimateCameraState extends State { ), ], ), - ], - ) - ], + ), + Expanded( + child: MaplibreMap( + onMapCreated: _onMapCreated, + initialCameraPosition: + const CameraPosition(target: LatLng(0.0, 0.0), zoom: 2), + styleString: "assets/osm_style.json", + ), + ), + ], + ), ); } } diff --git a/example/lib/move_camera.dart b/example/lib/move_camera_page.dart similarity index 83% rename from example/lib/move_camera.dart rename to example/lib/move_camera_page.dart index db69d1488..5d3006f3a 100644 --- a/example/lib/move_camera.dart +++ b/example/lib/move_camera_page.dart @@ -4,27 +4,17 @@ import 'package:flutter/material.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; -import 'page.dart'; - -class MoveCameraPage extends ExamplePage { - const MoveCameraPage({super.key}) - : super(const Icon(Icons.map), 'Camera control'); - - @override - Widget build(BuildContext context) { - return const MoveCamera(); - } -} - -class MoveCamera extends StatefulWidget { - const MoveCamera({super.key}); +class MoveCameraPage extends StatefulWidget { + const MoveCameraPage({super.key}); @override - State createState() => MoveCameraState(); + State createState() => _MoveCameraPageState(); } -class MoveCameraState extends State { +class _MoveCameraPageState extends State { late MaplibreMapController mapController; void _onMapCreated(MaplibreMapController controller) { @@ -33,26 +23,16 @@ class MoveCameraState extends State { @override Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Center( - child: SizedBox( - width: 300.0, - height: 200.0, - child: MaplibreMap( - onMapCreated: _onMapCreated, - onCameraIdle: () => debugPrint("onCameraIdle"), - initialCameraPosition: - const CameraPosition(target: LatLng(0.0, 0.0)), - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Column( + return ExampleScaffold( + page: ExamplePage.moveCamera, + body: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: const EdgeInsets.all(8), + child: Wrap( + alignment: WrapAlignment.center, children: [ TextButton( onPressed: () { @@ -114,10 +94,6 @@ class MoveCameraState extends State { }, child: const Text('scrollBy'), ), - ], - ), - Column( - children: [ TextButton( onPressed: () { mapController.moveCamera( @@ -179,9 +155,18 @@ class MoveCameraState extends State { ), ], ), - ], - ) - ], + ), + Expanded( + child: MaplibreMap( + onMapCreated: _onMapCreated, + onCameraIdle: () => debugPrint("onCameraIdle"), + initialCameraPosition: + const CameraPosition(target: LatLng(0.0, 0.0), zoom: 2), + styleString: "assets/osm_style.json", + ), + ), + ], + ), ); } } diff --git a/example/lib/no_location_permission_page.dart b/example/lib/no_location_permission_page.dart index 4ab7b84ae..c2c142b11 100644 --- a/example/lib/no_location_permission_page.dart +++ b/example/lib/no_location_permission_page.dart @@ -1,39 +1,28 @@ import 'package:flutter/material.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; -import 'page.dart'; - -class NoLocationPermissionPage extends ExamplePage { - const NoLocationPermissionPage({super.key}) - : super( - const Icon(Icons.gps_off), - 'Using a map without user location/permission', - needsLocationPermission: false, - ); - - @override - Widget build(BuildContext context) { - return const NoLocationPermissionBody(); - } -} - -class NoLocationPermissionBody extends StatefulWidget { - const NoLocationPermissionBody({super.key}); +class NoLocationPermissionPage extends StatefulWidget { + const NoLocationPermissionPage({super.key}); @override - State createState() => - _NoLocationPermissionBodyState(); + State createState() => + _NoLocationPermissionPageState(); } -class _NoLocationPermissionBodyState extends State { +class _NoLocationPermissionPageState extends State { @override Widget build(BuildContext context) { - return MaplibreMap( - initialCameraPosition: const CameraPosition( - target: LatLng(-33.852, 151.211), - zoom: 11.0, + return ExampleScaffold( + page: ExamplePage.noLocationPermission, + body: MaplibreMap( + initialCameraPosition: const CameraPosition( + target: LatLng(-33.852, 151.211), + zoom: 11.0, + ), + styleString: "assets/osm_style.json", ), - styleString: "assets/osm_style.json", ); } } diff --git a/example/lib/offline_region_map.dart b/example/lib/offline_region_map_page.dart similarity index 72% rename from example/lib/offline_region_map.dart rename to example/lib/offline_region_map_page.dart index 52c9160d0..b127e2d05 100644 --- a/example/lib/offline_region_map.dart +++ b/example/lib/offline_region_map_page.dart @@ -1,24 +1,21 @@ import 'package:flutter/material.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/offline_regions_page.dart'; -import 'offline_regions.dart'; - -class OfflineRegionMap extends StatefulWidget { - const OfflineRegionMap(this.item, {super.key}); +class OfflineRegionMapPage extends StatefulWidget { + const OfflineRegionMapPage(this.item, {super.key}); final OfflineRegionListItem item; @override - State createState() => _OfflineRegionMapState(); + State createState() => _OfflineRegionMapPageState(); } -class _OfflineRegionMapState extends State { +class _OfflineRegionMapPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar( - title: Text('Offline Region: ${widget.item.name}'), - ), + appBar: AppBar(title: Text('Offline Region: ${widget.item.name}')), body: MaplibreMap( initialCameraPosition: CameraPosition( target: _center, diff --git a/example/lib/offline_regions.dart b/example/lib/offline_regions_page.dart similarity index 66% rename from example/lib/offline_regions.dart rename to example/lib/offline_regions_page.dart index b9e4573e0..a9fcff86a 100644 --- a/example/lib/offline_regions.dart +++ b/example/lib/offline_regions_page.dart @@ -1,9 +1,9 @@ import 'package:collection/collection.dart' show IterableExtension; import 'package:flutter/material.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; - -import 'offline_region_map.dart'; -import 'page.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/offline_region_map_page.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; final LatLngBounds hawaiiBounds = LatLngBounds( southwest: const LatLng(17.26672, -161.14746), @@ -44,7 +44,7 @@ final List regionDefinitions = [ final List regionNames = ['Hawaii', 'Santiago', 'Auckland']; class OfflineRegionListItem { - OfflineRegionListItem({ + const OfflineRegionListItem({ required this.offlineRegionDefinition, required this.downloadedId, required this.isDownloading, @@ -97,24 +97,14 @@ final List allRegions = [ ), ]; -class OfflineRegionsPage extends ExamplePage { - const OfflineRegionsPage({super.key}) - : super(const Icon(Icons.map), 'Offline Regions'); +class OfflineRegionsPage extends StatefulWidget { + const OfflineRegionsPage({super.key}); @override - Widget build(BuildContext context) { - return const OfflineRegionBody(); - } + State createState() => _OfflineRegionsBodyState(); } -class OfflineRegionBody extends StatefulWidget { - const OfflineRegionBody({super.key}); - - @override - State createState() => _OfflineRegionsBodyState(); -} - -class _OfflineRegionsBodyState extends State { +class _OfflineRegionsBodyState extends State { final List _items = []; @override @@ -125,58 +115,61 @@ class _OfflineRegionsBodyState extends State { @override Widget build(BuildContext context) { - return Stack( - children: [ - ListView.builder( - padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 8), - itemCount: _items.length, - itemBuilder: (context, index) => Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton( - icon: const Icon(Icons.map), - onPressed: () => _goToMap(_items[index]), - ), - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - _items[index].name, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - Text( - 'Est. tiles: ${_items[index].estimatedTiles}', - style: const TextStyle( - fontSize: 16, + return ExampleScaffold( + page: ExamplePage.offlineRegions, + body: Stack( + children: [ + ListView.builder( + padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 8), + itemCount: _items.length, + itemBuilder: (context, index) => Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + icon: const Icon(Icons.map), + onPressed: () => _goToMap(_items[index]), + ), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _items[index].name, + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + ), ), - ), - ], - ), - const Spacer(), - _items[index].isDownloading - ? const SizedBox( - height: 16, - width: 16, - child: CircularProgressIndicator(), - ) - : IconButton( - icon: Icon( - _items[index].isDownloaded - ? Icons.delete - : Icons.file_download, + Text( + 'Est. tiles: ${_items[index].estimatedTiles}', + style: const TextStyle( + fontSize: 16, ), - onPressed: _items[index].isDownloaded - ? () => _deleteRegion(_items[index], index) - : () => _downloadRegion(_items[index], index), ), - ], + ], + ), + const Spacer(), + _items[index].isDownloading + ? const SizedBox( + height: 16, + width: 16, + child: CircularProgressIndicator(), + ) + : IconButton( + icon: Icon( + _items[index].isDownloaded + ? Icons.delete + : Icons.file_download, + ), + onPressed: _items[index].isDownloaded + ? () => _deleteRegion(_items[index], index) + : () => _downloadRegion(_items[index], index), + ), + ], + ), ), - ), - ], + ], + ), ); } @@ -258,7 +251,7 @@ class _OfflineRegionsBodyState extends State { _goToMap(OfflineRegionListItem item) { Navigator.of(context).push( MaterialPageRoute( - builder: (_) => OfflineRegionMap(item), + builder: (_) => OfflineRegionMapPage(item), ), ); } diff --git a/example/lib/page.dart b/example/lib/page.dart deleted file mode 100644 index 45b88af19..000000000 --- a/example/lib/page.dart +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; - -abstract class ExamplePage extends StatelessWidget { - const ExamplePage( - this.leading, - this.title, { - this.needsLocationPermission = true, - super.key, - }); - - final Widget leading; - final String title; - final bool needsLocationPermission; -} diff --git a/example/lib/place_source.dart b/example/lib/place_source.dart deleted file mode 100644 index 40b2ed0da..000000000 --- a/example/lib/place_source.dart +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:maplibre_gl/maplibre_gl.dart'; - -import 'page.dart'; - -class PlaceSourcePage extends ExamplePage { - const PlaceSourcePage({super.key}) - : super(const Icon(Icons.place), 'Place source'); - - @override - Widget build(BuildContext context) { - return const PlaceSymbolBody(); - } -} - -class PlaceSymbolBody extends StatefulWidget { - const PlaceSymbolBody({super.key}); - - @override - State createState() => PlaceSymbolBodyState(); -} - -class PlaceSymbolBodyState extends State { - PlaceSymbolBodyState(); - - static const sourceId = 'sydney_source'; - static const layerId = 'sydney_layer'; - - bool sourceAdded = false; - bool layerAdded = false; - bool imageFlag = false; - late MaplibreMapController controller; - - void _onMapCreated(MaplibreMapController controller) { - this.controller = controller; - } - - @override - void dispose() { - super.dispose(); - } - - /// Adds an asset image as a source to the currently displayed style - Future addImageSourceFromAsset( - String imageSourceId, String assetName) async { - final ByteData bytes = await rootBundle.load(assetName); - final Uint8List list = bytes.buffer.asUint8List(); - return controller.addImageSource( - imageSourceId, - list, - const LatLngQuad( - bottomRight: LatLng(-33.86264728692581, 151.19916915893555), - bottomLeft: LatLng(-33.86264728692581, 151.2288236618042), - topLeft: LatLng(-33.84322353475214, 151.2288236618042), - topRight: LatLng(-33.84322353475214, 151.19916915893555), - ), - ); - } - - Future removeImageSource(String imageSourceId) { - return controller.removeSource(imageSourceId); - } - - Future addLayer(String imageLayerId, String imageSourceId) { - if (layerAdded) { - removeLayer(imageLayerId); - } - setState(() => layerAdded = true); - return controller.addImageLayer(imageLayerId, imageSourceId); - } - - Future addLayerBelow( - String imageLayerId, String imageSourceId, String belowLayerId) { - if (layerAdded) { - removeLayer(imageLayerId); - } - setState(() => layerAdded = true); - return controller.addImageLayerBelow( - imageLayerId, imageSourceId, belowLayerId); - } - - Future removeLayer(String imageLayerId) { - setState(() => layerAdded = false); - return controller.removeLayer(imageLayerId); - } - - Future updateImageSourceFromAsset( - String imageSourceId, String assetName) async { - final ByteData bytes = await rootBundle.load(assetName); - final Uint8List list = bytes.buffer.asUint8List(); - return controller.updateImageSource(imageSourceId, list, null); - } - - String pickImage() { - return imageFlag ? 'assets/sydney0.png' : 'assets/sydney1.png'; - } - - @override - Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Center( - child: SizedBox( - width: 300.0, - height: 200.0, - child: MaplibreMap( - onMapCreated: _onMapCreated, - initialCameraPosition: const CameraPosition( - target: LatLng(-33.852, 151.211), - zoom: 11.0, - ), - ), - ), - ), - Expanded( - child: SingleChildScrollView( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Column( - children: [ - TextButton( - onPressed: sourceAdded - ? null - : () { - addImageSourceFromAsset(sourceId, pickImage()) - .then((value) { - setState(() => sourceAdded = true); - }); - }, - child: const Text('Add source (asset image)'), - ), - TextButton( - onPressed: sourceAdded - ? () async { - await removeLayer(layerId); - removeImageSource(sourceId).then((value) { - setState(() => sourceAdded = false); - }); - } - : null, - child: const Text('Remove source (asset image)'), - ), - TextButton( - onPressed: sourceAdded - ? () => addLayer(layerId, sourceId) - : null, - child: const Text('Show layer'), - ), - TextButton( - onPressed: sourceAdded - ? () => addLayerBelow(layerId, sourceId, 'water') - : null, - child: const Text('Show layer below water'), - ), - TextButton( - onPressed: - sourceAdded ? () => removeLayer(layerId) : null, - child: const Text('Hide layer'), - ), - TextButton( - onPressed: sourceAdded - ? () async { - setState(() => imageFlag = !imageFlag); - updateImageSourceFromAsset(sourceId, pickImage()); - } - : null, - child: const Text('Change image'), - ), - ], - ), - ], - ), - ), - ), - ], - ); - } -} diff --git a/example/lib/scrolling_map.dart b/example/lib/scrolling_map.dart deleted file mode 100644 index e7e6f9fd5..000000000 --- a/example/lib/scrolling_map.dart +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/foundation.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; // ignore: unnecessary_import -import 'package:maplibre_gl/maplibre_gl.dart'; -import 'package:maplibre_gl_example/util.dart'; - -import 'page.dart'; - -class ScrollingMapPage extends ExamplePage { - const ScrollingMapPage({super.key}) - : super(const Icon(Icons.map), 'Scrolling map'); - - @override - Widget build(BuildContext context) { - return const ScrollingMapBody(); - } -} - -class ScrollingMapBody extends StatefulWidget { - const ScrollingMapBody({super.key}); - - @override - State createState() => _ScrollingMapBodyState(); -} - -class _ScrollingMapBodyState extends State { - late MaplibreMapController controllerOne; - late MaplibreMapController controllerTwo; - - final LatLng center = const LatLng(32.080664, 34.9563837); - - @override - Widget build(BuildContext context) { - return ListView( - children: [ - Card( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 30.0), - child: Column( - children: [ - const Padding( - padding: EdgeInsets.only(bottom: 12.0), - child: Text('This map consumes all touch events.'), - ), - Center( - child: SizedBox( - width: 300.0, - height: 300.0, - child: MaplibreMap( - onMapCreated: onMapCreatedOne, - onStyleLoadedCallback: () => onStyleLoaded(controllerOne), - initialCameraPosition: CameraPosition( - target: center, - zoom: 11.0, - ), - gestureRecognizers: >{ - Factory( - () => EagerGestureRecognizer(), - ), - }, - ), - ), - ), - ], - ), - ), - ), - Card( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 30.0), - child: Column( - children: [ - const Text('This map doesn\'t consume the vertical drags.'), - const Padding( - padding: EdgeInsets.only(bottom: 12.0), - child: - Text('It still gets other gestures (e.g scale or tap).'), - ), - Center( - child: SizedBox( - width: 300.0, - height: 300.0, - child: MaplibreMap( - onMapCreated: onMapCreatedTwo, - onStyleLoadedCallback: () => onStyleLoaded(controllerTwo), - initialCameraPosition: CameraPosition( - target: center, - zoom: 11.0, - ), - gestureRecognizers: >{ - Factory( - () => ScaleGestureRecognizer(), - ), - }, - ), - ), - ), - ], - ), - ), - ), - ], - ); - } - - void onMapCreatedOne(MaplibreMapController controller) { - controllerOne = controller; - } - - void onMapCreatedTwo(MaplibreMapController controller) { - controllerTwo = controller; - } - - void onStyleLoaded(MaplibreMapController controller) async { - await addImageFromAsset( - controller, "custom-marker", "assets/symbols/custom-marker.png"); - controller.addSymbol(SymbolOptions( - geometry: LatLng( - center.latitude, - center.longitude, - ), - iconImage: "custom-marker")); - controller.addLine( - const LineOptions( - geometry: [ - LatLng(-33.86711, 151.1947171), - LatLng(-33.86711, 151.1947171), - LatLng(-32.86711, 151.1947171), - LatLng(-33.86711, 152.1947171), - ], - lineColor: "#ff0000", - lineWidth: 7.0, - lineOpacity: 0.5, - ), - ); - } -} diff --git a/example/lib/scrolling_map_page.dart b/example/lib/scrolling_map_page.dart new file mode 100644 index 000000000..9be13d7b0 --- /dev/null +++ b/example/lib/scrolling_map_page.dart @@ -0,0 +1,139 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; // ignore: unnecessary_import +import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/util.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; + +class ScrollingMapPage extends StatefulWidget { + const ScrollingMapPage({super.key}); + + @override + State createState() => _ScrollingMapPageState(); +} + +class _ScrollingMapPageState extends State { + late MaplibreMapController controllerOne; + late MaplibreMapController controllerTwo; + + final LatLng center = const LatLng(32.080664, 34.9563837); + + @override + Widget build(BuildContext context) { + return ExampleScaffold( + page: ExamplePage.scrolling, + body: ListView( + children: [ + Card( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 30.0), + child: Column( + children: [ + const Padding( + padding: EdgeInsets.only(bottom: 12.0), + child: Text('This map consumes all touch events.'), + ), + Center( + child: SizedBox( + width: 300.0, + height: 300.0, + child: MaplibreMap( + onMapCreated: onMapCreatedOne, + onStyleLoadedCallback: () => + onStyleLoaded(controllerOne), + initialCameraPosition: CameraPosition( + target: center, + zoom: 11.0, + ), + gestureRecognizers: >{ + Factory( + () => EagerGestureRecognizer(), + ), + }, + ), + ), + ), + ], + ), + ), + ), + Card( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 30.0), + child: Column( + children: [ + const Text('This map doesn\'t consume the vertical drags.'), + const Padding( + padding: EdgeInsets.only(bottom: 12.0), + child: Text( + 'It still gets other gestures (e.g scale or tap).'), + ), + Center( + child: SizedBox( + width: 300.0, + height: 300.0, + child: MaplibreMap( + onMapCreated: onMapCreatedTwo, + onStyleLoadedCallback: () => + onStyleLoaded(controllerTwo), + initialCameraPosition: CameraPosition( + target: center, + zoom: 11.0, + ), + gestureRecognizers: >{ + Factory( + () => ScaleGestureRecognizer(), + ), + }, + ), + ), + ), + ], + ), + ), + ), + ], + ), + ); + } + + void onMapCreatedOne(MaplibreMapController controller) { + controllerOne = controller; + } + + void onMapCreatedTwo(MaplibreMapController controller) { + controllerTwo = controller; + } + + void onStyleLoaded(MaplibreMapController controller) async { + await addImageFromAsset( + controller, "custom-marker", "assets/symbols/custom-marker.png"); + controller.addSymbol(SymbolOptions( + geometry: LatLng( + center.latitude, + center.longitude, + ), + iconImage: "custom-marker")); + controller.addLine( + const LineOptions( + geometry: [ + LatLng(-33.86711, 151.1947171), + LatLng(-33.86711, 151.1947171), + LatLng(-32.86711, 151.1947171), + LatLng(-33.86711, 152.1947171), + ], + lineColor: "#ff0000", + lineWidth: 7.0, + lineOpacity: 0.5, + ), + ); + } +} diff --git a/example/lib/set_map_bounds_page.dart b/example/lib/set_map_bounds_page.dart new file mode 100644 index 000000000..ecf1b72f3 --- /dev/null +++ b/example/lib/set_map_bounds_page.dart @@ -0,0 +1,73 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; +import 'package:maplibre_gl_example/main.dart'; + +class SetMapBoundsPage extends StatefulWidget { + const SetMapBoundsPage({super.key}); + + @override + State createState() => _SetMapBoundsPageState(); +} + +class _SetMapBoundsPageState extends State { + late MaplibreMapController mapController; + + void _onMapCreated(MaplibreMapController controller) { + mapController = controller; + } + + @override + Widget build(BuildContext context) { + return ExampleScaffold( + page: ExamplePage.setMapBounds, + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8), + child: Wrap( + alignment: WrapAlignment.center, + children: [ + TextButton( + onPressed: () async { + await mapController.setCameraBounds( + west: 5.98865807458, + south: 47.3024876979, + east: 15.0169958839, + north: 54.983104153, + padding: 25, + ); + }, + child: const Text('Set bounds to Germany'), + ), + TextButton( + onPressed: () async { + await mapController.setCameraBounds( + west: -18, + south: -40, + east: 54, + north: 40, + padding: 25, + ); + }, + child: const Text('Set bounds to Africa'), + ), + ], + ), + ), + Expanded( + child: MaplibreMap( + onMapCreated: _onMapCreated, + initialCameraPosition: + const CameraPosition(target: LatLng(0.0, 0.0)), + ), + ), + ], + ), + ); + } +} diff --git a/example/lib/map_ui.dart b/example/lib/user_interface_page.dart similarity index 86% rename from example/lib/map_ui.dart rename to example/lib/user_interface_page.dart index cfabc6ec4..b4437cdd6 100644 --- a/example/lib/map_ui.dart +++ b/example/lib/user_interface_page.dart @@ -4,35 +4,26 @@ import 'dart:math'; -import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; - -import 'page.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; +import 'package:maplibre_gl_example/main.dart'; final LatLngBounds sydneyBounds = LatLngBounds( southwest: const LatLng(-34.022631, 150.620685), northeast: const LatLng(-33.571835, 151.325952), ); -class MapUiPage extends ExamplePage { - const MapUiPage({super.key}) : super(const Icon(Icons.map), 'User interface'); +class UserInterfacePage extends StatefulWidget { + const UserInterfacePage({super.key}); @override - Widget build(BuildContext context) { - return const MapUiBody(); - } + State createState() => _UserInterfacePageState(); } -class MapUiBody extends StatefulWidget { - const MapUiBody({super.key}); - - @override - State createState() => MapUiBodyState(); -} - -class MapUiBodyState extends State { - MapUiBodyState(); +class _UserInterfacePageState extends State { + _UserInterfacePageState(); static const CameraPosition _kInitialPosition = CameraPosition( target: LatLng(-33.852, 151.211), @@ -413,12 +404,6 @@ class MapUiBodyState extends State { if (mapController != null) { listViewChildren.addAll( [ - Text('camera bearing: ${_position.bearing}'), - Text('camera target: ${_position.target.latitude.toStringAsFixed(4)},' - '${_position.target.longitude.toStringAsFixed(4)}'), - Text('camera zoom: ${_position.zoom}'), - Text('camera tilt: ${_position.tilt}'), - Text(_isMoving ? '(Camera moving)' : '(Camera idle)'), _mapSizeToggler(), _queryFilterToggler(), _compassToggler(), @@ -439,22 +424,52 @@ class MapUiBodyState extends State { ], ); } - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Center( - child: SizedBox( - width: _mapExpanded ? null : 300.0, - height: 200.0, - child: maplibreMap, + return ExampleScaffold( + page: ExamplePage.userInterface, + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8), + child: Wrap( + alignment: WrapAlignment.center, + children: listViewChildren, + ), ), - ), - Expanded( - child: ListView( - children: listViewChildren, + Expanded( + child: Stack( + children: [ + SizedBox( + width: _mapExpanded ? null : 300.0, + child: maplibreMap, + ), + Container( + padding: const EdgeInsets.all(8), + alignment: Alignment.topCenter, + child: Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text('Camera', + style: TextStyle(fontWeight: FontWeight.bold)), + Text( + 'bearing: ${_position.bearing}, target: ${_position.target.latitude.toStringAsFixed(4)},' + '${_position.target.longitude.toStringAsFixed(4)}\n' + 'zoom: ${_position.zoom}, camera tilt: ${_position.tilt}', + textAlign: TextAlign.center, + ), + Text(_isMoving ? '(Camera moving)' : '(Camera idle)'), + ], + ), + ), + ), + ), + ], + ), ), - ) - ], + ], + ), ); } diff --git a/example/lib/sources.dart b/example/lib/various_sources_page.dart similarity index 84% rename from example/lib/sources.dart rename to example/lib/various_sources_page.dart index e2538d4dd..3f359f877 100644 --- a/example/lib/sources.dart +++ b/example/lib/various_sources_page.dart @@ -1,8 +1,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; - -import 'page.dart'; +import 'package:maplibre_gl_example/main.dart'; +import 'package:maplibre_gl_example/common/example_scaffold.dart'; class StyleInfo { final String name; @@ -10,30 +10,22 @@ class StyleInfo { final Future Function(MaplibreMapController) addDetails; final CameraPosition position; - const StyleInfo( - {required this.name, - required this.baseStyle, - required this.addDetails, - required this.position}); + const StyleInfo({ + required this.name, + required this.baseStyle, + required this.addDetails, + required this.position, + }); } -class Sources extends ExamplePage { - const Sources({super.key}) : super(const Icon(Icons.map), 'Various Sources'); +class VariousSourcesPage extends StatefulWidget { + const VariousSourcesPage({super.key}); @override - Widget build(BuildContext context) { - return const FullMap(); - } + State createState() => _VariousSourcesPageState(); } -class FullMap extends StatefulWidget { - const FullMap({super.key}); - - @override - State createState() => FullMapState(); -} - -class FullMapState extends State { +class _VariousSourcesPageState extends State { MaplibreMapController? controller; final watercolorRasterId = "watercolorRaster"; int selectedStyleId = 0; @@ -319,41 +311,43 @@ class FullMapState extends State { final nextName = _stylesAndLoaders[(selectedStyleId + 1) % _stylesAndLoaders.length] .name; - return Scaffold( - floatingActionButton: Padding( - padding: const EdgeInsets.all(32.0), - child: FloatingActionButton.extended( - icon: const Icon(Icons.swap_horiz), - label: SizedBox( - width: 120, child: Center(child: Text("To $nextName"))), - onPressed: () => setState( - () => selectedStyleId = - (selectedStyleId + 1) % _stylesAndLoaders.length, - ), + return ExampleScaffold( + page: ExamplePage.variousSources, + floatingActionButton: Padding( + padding: const EdgeInsets.all(32.0), + child: FloatingActionButton.extended( + icon: const Icon(Icons.swap_horiz), + label: + SizedBox(width: 120, child: Center(child: Text("To $nextName"))), + onPressed: () => setState( + () => selectedStyleId = + (selectedStyleId + 1) % _stylesAndLoaders.length, ), ), - body: Stack( - children: [ - MaplibreMap( - styleString: styleInfo.baseStyle, - onMapCreated: _onMapCreated, - initialCameraPosition: styleInfo.position, - onStyleLoadedCallback: _onStyleLoadedCallback, - ), - Container( - padding: const EdgeInsets.all(8), - alignment: Alignment.topCenter, - child: Card( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - "Current source: ${styleInfo.name}", - style: Theme.of(context).textTheme.titleLarge, - ), + ), + body: Stack( + children: [ + MaplibreMap( + styleString: styleInfo.baseStyle, + onMapCreated: _onMapCreated, + initialCameraPosition: styleInfo.position, + onStyleLoadedCallback: _onStyleLoadedCallback, + ), + Container( + padding: const EdgeInsets.all(8), + alignment: Alignment.topCenter, + child: Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "Current source: ${styleInfo.name}", + style: Theme.of(context).textTheme.titleLarge, ), ), ), - ], - )); + ), + ], + ), + ); } } diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 9cc1af292..621a65326 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -12,6 +12,8 @@ environment: dependencies: flutter: sdk: flutter + flutter_web_plugins: + sdk: flutter maplibre_gl: ^0.19.0 location: ^5.0.3 @@ -19,6 +21,7 @@ dependencies: http: ^1.1.0 collection: ^1.17.1 device_info_plus: ^9.0.2 + url_launcher: ^6.0.0 dev_dependencies: very_good_analysis: ^5.0.0 diff --git a/example/web/index.html b/example/web/index.html index 101bc57cd..1553fa7d2 100644 --- a/example/web/index.html +++ b/example/web/index.html @@ -4,6 +4,7 @@ + diff --git a/maplibre_gl_web/lib/src/maplibre_web_gl_platform.dart b/maplibre_gl_web/lib/src/maplibre_web_gl_platform.dart index 0b938afef..538a58775 100644 --- a/maplibre_gl_web/lib/src/maplibre_web_gl_platform.dart +++ b/maplibre_gl_web/lib/src/maplibre_web_gl_platform.dart @@ -344,7 +344,7 @@ class MaplibreMapController extends MapLibreGlPlatform 'coordinates': feature.geometry.coordinates, }, 'properties': feature.properties, - 'source': feature.source, + 'source': feature.annotationSource, }) .toList(); }