diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..c787c707 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,204 @@ +name: CI + +on: + push: + branches: [ main ] + pull_request: + workflow_dispatch: + +# Ensure that new pushes/updates cancel running jobs +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + format: + name: "Check formatting" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + cache: true + - name: Lint analysis + run: dart format --set-exit-if-changed . + lint: + name: "Static code analysis" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + cache: true + - name: "Get Flutter dependencies" + run: dart pub get + - name: Lint analysis + run: dart analyze --fatal-warnings --fatal-infos +# integration-test-ios: +# name: "Integration Tests on iOS" +# runs-on: macOS-latest +# defaults: +# run: +# working-directory: example +# steps: +# - uses: actions/checkout@v4 +# - uses: futureware-tech/simulator-action@v3 +# with: +# model: 'iPhone 15' +# - uses: subosito/flutter-action@v2 +# with: +# cache: true +# - run: flutter pub get +# - run: flutter test integration_test --no-pub -r expanded + integration-test-android: + name: "Integration Tests on Android" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + api-level: [ 23 ] # TODO add 34 + timeout-minutes: 30 + defaults: + run: + working-directory: example + steps: + - uses: actions/checkout@v4 + - name: Setup Flutter SDK + uses: subosito/flutter-action@v2 + with: + cache: true + - uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: 'gradle' + - name: "Get Flutter dependencies" + run: flutter pub get + - name: Run integration tests + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: ${{ matrix.api-level }} + arch: x86_64 + emulator-boot-timeout: 1800 # 30 minutes + script: cd example && flutter test integration_test -r expanded --timeout=none + integration-test-web: + name: "Integration Tests on Web" + runs-on: ubuntu-latest + timeout-minutes: 30 + defaults: + run: + working-directory: example + steps: + - uses: actions/checkout@v4 + - name: Setup Flutter SDK + uses: subosito/flutter-action@v2 + with: + cache: true + - name: "Get Flutter dependencies" + run: flutter pub get + - name: "Start chromedriver" + run: | + sudo chromedriver --enable --port=4444 & + - name: "Run integration tests" + run: | + flutter drive \ + --driver=test_driver/integration_test.dart \ + --target=integration_test/smoke_test.dart \ + -d web-server \ + --release \ + --browser-name=chrome + score: + name: "Package score" + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + cache: true + - name: Install pana + run: dart pub global activate pana + - name: Check package score + run: pana --exit-code-threshold 0 --no-dartdoc . + build-android: + name: "Build Android APK" + runs-on: ubuntu-latest + defaults: + run: + working-directory: example + strategy: + fail-fast: false + matrix: + sdk: [ '3.22.3', '' ] + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + flutter-version: ${{ matrix.sdk }} + cache: true + - uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: 'gradle' + - name: "Get Flutter dependencies" + run: dart pub get + - name: Build example APK + run: flutter build apk + - name: Upload apk as artifact + uses: actions/upload-artifact@v4 + if: ${{ matrix.sdk == '' }} + with: + name: maplibre-flutter-demo.apk + path: example/build/app/outputs/flutter-apk/app-release.apk +# build-iOS: +# name: Build iOS package +# runs-on: macos-latest +# timeout-minutes: 30 +# defaults: +# run: +# working-directory: example +# strategy: +# fail-fast: false +# matrix: +# sdk: [ '3.22.3', '' ] +# steps: +# - uses: actions/checkout@v4 +# - uses: subosito/flutter-action@v2 +# with: +# flutter-version: ${{ matrix.sdk }} +# cache: true +# - uses: maxim-lobanov/setup-cocoapods@v1 +# with: +# podfile-path: example/ios/Podfile.lock +# - name: "Get Flutter dependencies" +# run: dart pub get +# - name: Build iOS package +# run: flutter build ios --simulator +# - name: Upload Runner.app as artifact +# if: ${{ matrix.sdk == '' }} +# uses: actions/upload-artifact@v4 +# with: +# name: maplibre-flutter-demo.app +# path: example/build/ios/iphonesimulator + build-web: + name: "Build web" + runs-on: ubuntu-latest + defaults: + run: + working-directory: example + strategy: + fail-fast: false + matrix: + sdk: [ '3.22.3', '' ] + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + flutter-version: ${{ matrix.sdk }} + cache: true + - name: "Get Flutter dependencies" + run: dart pub get + - name: Build web + run: flutter build web diff --git a/.github/workflows/flutter-beta.yml b/.github/workflows/flutter-beta.yml index fb0aa6bc..d7e6f2c4 100644 --- a/.github/workflows/flutter-beta.yml +++ b/.github/workflows/flutter-beta.yml @@ -32,28 +32,28 @@ jobs: with: name: maplibre-flutter-demo.apk path: example/build/app/outputs/flutter-apk/app-release.apk - build-iOS: - name: Build iOS package - runs-on: macos-latest - defaults: - run: - working-directory: example - steps: - - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2 - with: - channel: ${{ env.FLUTTER_CHANNEL }} - cache: true - - uses: maxim-lobanov/setup-cocoapods@v1 - with: - podfile-path: example/ios/Podfile.lock - - name: Build iOS package - run: flutter build ios --simulator - - name: Upload Runner.app as artifact - uses: actions/upload-artifact@v4 - with: - name: maplibre-flutter-demo.app - path: example/build/ios/iphonesimulator +# build-iOS: +# name: Build iOS package +# runs-on: macos-latest +# defaults: +# run: +# working-directory: example +# steps: +# - uses: actions/checkout@v4 +# - uses: subosito/flutter-action@v2 +# with: +# channel: ${{ env.FLUTTER_CHANNEL }} +# cache: true +# - uses: maxim-lobanov/setup-cocoapods@v1 +# with: +# podfile-path: example/ios/Podfile.lock +# - name: Build iOS package +# run: flutter build ios --simulator +# - name: Upload Runner.app as artifact +# uses: actions/upload-artifact@v4 +# with: +# name: maplibre-flutter-demo.app +# path: example/build/ios/iphonesimulator build-web: name: "Build web" runs-on: ubuntu-latest diff --git a/.github/workflows/flutter-ci.yml b/.github/workflows/flutter-ci.yml deleted file mode 100644 index 8e760e52..00000000 --- a/.github/workflows/flutter-ci.yml +++ /dev/null @@ -1,116 +0,0 @@ -name: Flutter CI - -on: - push: - branches: [ main ] - pull_request: - workflow_dispatch: - -# Ensure that new pushes/updates cancel running jobs -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -jobs: - format: - name: "Check formatting" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2 - with: - cache: true - - name: Lint analysis - run: dart format --set-exit-if-changed . - lint: - name: "Static code analysis" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2 - with: - cache: true - - name: "Get flutter dependencies" - run: dart pub get - - name: Lint analysis - run: dart analyze --fatal-warnings --fatal-infos - build-android: - name: "Build Android APK" - runs-on: ubuntu-latest - defaults: - run: - working-directory: example -# strategy: -# fail-fast: false -# matrix: -# sdk: [ '3.22.3', '' ] - steps: - - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2 - with: - flutter-version: ${{ matrix.sdk }} - cache: true - - uses: actions/setup-java@v4 - with: - java-version: '17' - distribution: 'temurin' - cache: 'gradle' - - name: "Get flutter dependencies" - run: dart pub get - - name: Build example APK - run: flutter build apk - - name: Upload apk as artifact - uses: actions/upload-artifact@v4 - if: ${{ matrix.sdk == '' }} - with: - name: maplibre-flutter-demo.apk - path: example/build/app/outputs/flutter-apk/app-release.apk -# build-iOS: -# name: Build iOS package -# runs-on: macos-latest -# defaults: -# run: -# working-directory: example -# strategy: -# fail-fast: false -# matrix: -# sdk: [ '3.22.3', '' ] -# steps: -# - uses: actions/checkout@v4 -# - uses: subosito/flutter-action@v2 -# with: -# flutter-version: ${{ matrix.sdk }} -# cache: true -# - uses: maxim-lobanov/setup-cocoapods@v1 -# with: -# podfile-path: example/ios/Podfile.lock -# - name: "Get flutter dependencies" -# run: dart pub get -# - name: Build iOS package -# run: flutter build ios --simulator -# - name: Upload Runner.app as artifact -# if: ${{ matrix.sdk == '' }} -# uses: actions/upload-artifact@v4 -# with: -# name: maplibre-flutter-demo.app -# path: example/build/ios/iphonesimulator - build-web: - name: "Build web" - runs-on: ubuntu-latest - defaults: - run: - working-directory: example -# strategy: -# fail-fast: false -# matrix: -# sdk: [ '3.22.3', '' ] - steps: - - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2 - with: - flutter-version: ${{ matrix.sdk }} - cache: true - - name: "Get flutter dependencies" - run: dart pub get - - name: Build web - run: flutter build web diff --git a/.gitignore b/.gitignore index 0299f8f7..6fcf232e 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ pubspec.lock **/doc/api/ .dart_tool/ build/ + +# converage +**/coverage/ \ No newline at end of file diff --git a/README.md b/README.md index 71bda0c3..effcb29e 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![likes](https://img.shields.io/pub/likes/maplibre?logo=flutter)](https://pub.dev/packages/maplibre) [![Pub Points](https://img.shields.io/pub/points/maplibre)](https://pub.dev/packages/maplibre/score) [![Pub Popularity](https://img.shields.io/pub/popularity/maplibre)](https://pub.dev/packages/maplibre) +[![style: very good analysis](https://img.shields.io/badge/style-very_good_analysis-B22C89.svg)](https://pub.dev/packages/very_good_analysis) [![GitHub last commit](https://img.shields.io/github/last-commit/josxha/flutter-maplibre)](https://github.com/josxha/flutter-maplibre) [![stars](https://badgen.net/github/stars/josxha/flutter-maplibre?label=stars&color=green&icon=github)](https://github.com/josxha/flutter-maplibre/stargazers) @@ -42,7 +43,7 @@ Check out the [hosted demo application](https://flutter-maplibre.pages.dev/demo) or dive into the [example app code](https://github.com/josxha/flutter-maplibre/tree/main/example/lib). -### Get started & Documentation +### Get Started & Documentation Visit the docs to learn how to get started with maplibre in your project: [Get Started](https://flutter-maplibre.pages.dev/docs/category/getting-started) @@ -51,7 +52,7 @@ If you want to know more about the classes and properties of the package, have a look at the [API docs](https://pub.dev/documentation/maplibre/latest/maplibre/maplibre-library.html). -### State of implementation +### State of Implementation This package is still a young package and in an early stage. While it offers a modern implementation, it currently lacks some @@ -62,14 +63,17 @@ functionality. [See our documentation to learn more.](https://flutter-maplibre.p If you need a feature or experience a bug you want to fix contributions are welcome. -### Run code generation +#### Run Code Generation ```bash +flutter pub global activate pigeon # only once dart run pigeon --input pigeons/pigeon.dart cp ios/Classes/Pigeon.g.swift macos/Classes/Pigeon.g.swift ``` -### Test with WebAssembly +#### Test with WebAssembly + +This package supports WebAssembly builds! 🥳 ```bash flutter pub global activate dhttpd # only once @@ -80,4 +84,4 @@ dhttpd '--headers=Cross-Origin-Embedder-Policy=credentialless;Cross-Origin-Opene or follow the [flutter documentation](https://docs.flutter.dev/platform-integration/web/wasm#serving-wasm-locally) -about WebAssembly builds. \ No newline at end of file +about Flutter WASM builds. \ No newline at end of file diff --git a/docs/docs/getting-started/add-dependency.md b/docs/docs/getting-started/add-dependency.md index 46574eee..587f7590 100644 --- a/docs/docs/getting-started/add-dependency.md +++ b/docs/docs/getting-started/add-dependency.md @@ -15,10 +15,11 @@ or add it directly as a dependency to your `pubspec.yaml` file and run ```yaml title="pubspec.yaml" dependencies: + # highlight-next-line maplibre: ^0.0.1 # use the latest version found on pub.dev ``` You can find the latest version of the package on -[pub.dev](https://pub.dev/packages/maplibre) or here: +[pub.dev](https://pub.dev/packages/maplibre) or here: [![Pub Version](https://img.shields.io/pub/v/maplibre)](https://pub.dev/packages/maplibre) diff --git a/docs/docs/getting-started/setup-android.md b/docs/docs/getting-started/setup-android.md index 8413ad60..d4ab1d91 100644 --- a/docs/docs/getting-started/setup-android.md +++ b/docs/docs/getting-started/setup-android.md @@ -10,13 +10,14 @@ Ensure that you are using Kotlin version **1.9.0** or newer. You can check the most recent Kotlin version on [kotlinlang.org](https://kotlinlang.org/docs/releases.html#release-details). -#### (new) Gradle with a declarative plugins block +### (new) Gradle with a declarative plugins block Open `android/settings.gradle` and set the Kotlin version like this: ```gradle title="android/settings.gradle" plugins { // ... + // highlight-next-line id "org.jetbrains.kotlin.android" version "1.9.0" apply false } ``` @@ -24,18 +25,40 @@ plugins { In case you can't find the `plugins {}` block your app still uses the old apply script method. -#### (old) In a legacy apply script gradle file: +### (old) In a legacy apply script gradle file Open `android/app/build.gradle` and set the Kotlin version like this: ```gradle title="android/app/build.gradle" buildscript { + // highlight-next-line ext.kotlin_version = '1.9.0' // ... } ``` -## Use the location feature +## Adjust the minSdk version of Android + +The `maplibre` package on Flutter +uses [platform views](https://docs.flutter.dev/platform-integration/android/platform-views) +to display the native map. This requires an Android SDK version of at least 23 +(Android 6.0). + +Open your `android/app/build.gradle` file and ensure that it is set to 23 or +higher. + +```gradle title="android/app/build.gradle" +android { + defaultConfig { + // ... + // highlight-next-line + minSdk = 23 // previously flutter.minSdkVersion + // ... + } +} +``` + +## Set the permissions If you want to show the user's location on the map you need to add the `ACCESS_COARSE_LOCATION` or `ACCESS_FINE_LOCATION` permission in the @@ -44,8 +67,10 @@ application manifest `android/app/src/main/AndroidManifest.xml`. ```xml title="android/app/src/main/AndroidManifest.xml" + // highlight-start + // highlight-end ``` diff --git a/docs/docs/getting-started/setup-web.md b/docs/docs/getting-started/setup-web.md index 91a1e6c2..190e5e9c 100644 --- a/docs/docs/getting-started/setup-web.md +++ b/docs/docs/getting-started/setup-web.md @@ -12,11 +12,11 @@ your `web/index.html` file: - - + + ``` diff --git a/docs/docs/getting-started/use-widget.md b/docs/docs/getting-started/use-widget.md index 9b5d967c..062da7cd 100644 --- a/docs/docs/getting-started/use-widget.md +++ b/docs/docs/getting-started/use-widget.md @@ -7,8 +7,9 @@ sidebar_position: 4 Import the maplibre package and use the `MapLibreMap` widget to display a map. -```dart +```dart title="map_screen.dart" import 'package:flutter/material.dart'; +// highlight-next-line import 'package:maplibre/maplibre.dart'; class MapScreen extends StatefulWidget { @@ -24,6 +25,7 @@ class MapScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( + // highlight-start body: MapLibreMap( onMapCreated: (controller) { // Don't add additional annotations here, @@ -34,6 +36,7 @@ class MapScreenState extends State { debugPrint('Map loaded 😎'); }, ), + // highlight-end ); } } diff --git a/docs/docs/map-styles.md b/docs/docs/map-styles.md index 7979e063..a5684c0d 100644 --- a/docs/docs/map-styles.md +++ b/docs/docs/map-styles.md @@ -4,6 +4,10 @@ sidebar_position: 3 # Map Styles +By default, the default MapLibre style gets used which shows only countries. +To change this use the style of a tile provider or create and use your own map +style. + Custom map styles can be used by setting the `style` parameter of the map options like so: @@ -33,7 +37,7 @@ to the url as a query parameter. For example: ```url -https://tiles.example.com/{z}/{x}/{y}.vector.pbf?api_key={your_key} +https://tiles.example.com/{z}/{x}/{y}.pbf?api_key={your_key} ``` ## Use existing styles @@ -42,6 +46,11 @@ Every tile provider provides map styles that you can use. Note, that the style needs to be compatible with the tile naming scheme. For example OpenMapTiles styles used by MapTiler are not compatible with Protomaps tiles. +Have a look +at [Awesome MapLibre](https://github.com/maplibre/awesome-maplibre#maptile-providers) +to get an overview over available tile +providers that support the MVT standard. + ## Create a custom style Map styles are defined in JSON. MapLibre offers a style editor diff --git a/docs/src/css/custom.css b/docs/src/css/custom.css index 09d19c4d..ad0d85d5 100644 --- a/docs/src/css/custom.css +++ b/docs/src/css/custom.css @@ -13,6 +13,7 @@ --ifm-color-primary-light: #2d66bb; --ifm-color-primary-lighter: #2f6bc3; --ifm-color-primary-lightest: #417bd1; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); } /* For readability concerns, you should choose a lighter palette in dark mode. */ @@ -24,4 +25,5 @@ --ifm-color-primary-light: #a8cbfe; --ifm-color-primary-lighter: #bbd6fe; --ifm-color-primary-lightest: #f4f9ff; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); } diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 849a4bcd..1f02c5b3 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -35,9 +35,8 @@ android { defaultConfig { applicationId = "com.github.josxha.maplibre_example" - // You can update the following values to match your application needs. - // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdk = flutter.minSdkVersion + // minSdk 21 required by maplibre, 23 required by Flutter platform views + minSdk = 23 // flutter.minSdkVersion targetSdk = flutter.targetSdkVersion versionCode = flutterVersionCode.toInteger() versionName = flutterVersionName diff --git a/example/integration_test/app.dart b/example/integration_test/app.dart new file mode 100644 index 00000000..5b476c36 --- /dev/null +++ b/example/integration_test/app.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:maplibre/maplibre.dart'; + +void main() { + runApp(const App()); +} + +class App extends StatelessWidget { + const App({ + super.key, + this.onMapCreated, + this.onStyleLoaded, + }); + + final MapCreatedCallback? onMapCreated; + final VoidCallback? onStyleLoaded; + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'MapLibre Demo', + home: Scaffold( + body: MapLibreMap( + options: MapOptions(center: Position(0, 0)), + onMapCreated: onMapCreated, + onStyleLoaded: onStyleLoaded, + ), + ), + ); + } +} diff --git a/example/integration_test/smoke_test.dart b/example/integration_test/smoke_test.dart new file mode 100644 index 00000000..90eb968a --- /dev/null +++ b/example/integration_test/smoke_test.dart @@ -0,0 +1,26 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:maplibre/maplibre.dart'; + +import 'app.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('controller', () { + testWidgets( + 'render map', + (tester) async { + // ignore: unused_local_variable + late final MapController ctrl; + final app = App( + onMapCreated: (controller) => ctrl = controller, + ); + await tester.pumpWidget(app); + await tester.pumpAndSettle(); + expect(tester.allWidgets.any((w) => w is MapLibreMap), true); + // await ctrl.jumpTo(center: Position(1, 1), bearing: 1, zoom: 1, tilt: 1); + }, + ); + }); +} diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 92f80e21..450f40f6 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -17,6 +17,10 @@ dependencies: path: ../ dev_dependencies: + flutter_test: + sdk: flutter + integration_test: + sdk: flutter very_good_analysis: ^6.0.0 flutter: diff --git a/example/test_driver/integration_test.dart b/example/test_driver/integration_test.dart new file mode 100644 index 00000000..b38629cc --- /dev/null +++ b/example/test_driver/integration_test.dart @@ -0,0 +1,3 @@ +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver();