Skip to content

Commit

Permalink
Merge pull request #470 from digitalfabrik/419-fix-map-orientation
Browse files Browse the repository at this point in the history
Reenable rotation to and from landscape mode on Android
  • Loading branch information
maxammann authored Jan 27, 2022
2 parents e52ce83 + 9086e8d commit 5f0564d
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 21 deletions.
3 changes: 3 additions & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion .idea/codeStyles/codeStyleConfig.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion frontend/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
Expand Down
27 changes: 13 additions & 14 deletions frontend/lib/entry_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import 'home/home_page.dart';
import 'intro_slides/intro_screen.dart';
import 'themes.dart';

const introRouteName = "/intro";
const homeRouteName = "/home";

class EntryWidget extends StatelessWidget {
const EntryWidget({Key? key}) : super(key: key);

Expand All @@ -26,24 +29,22 @@ class EntryWidget extends StatelessWidget {
if (snapshot.hasError && error != null) {
return ErrorMessage(error.toString());
} else if (snapshot.hasData && settings != null) {
final routes = <String, WidgetBuilder>{};
String? initialRoute;
final routes = <String, WidgetBuilder>{
introRouteName: (context) => IntroScreen(
onFinishedCallback: () => settings.setFirstStart(enabled: false),
),
homeRouteName: (context) => HomePage(
showVerification: configuration.showVerification,
)
};

if (settings.firstStart) {
routes.addAll(<String, WidgetBuilder>{
'/intro': (context) => IntroScreen(
onFinishedCallback: () => settings.setFirstStart(enabled: false),
),
});
initialRoute = '/intro';
}
final String initialRoute = settings.firstStart ? introRouteName : homeRouteName;

return MaterialApp(
title: 'Ehrenamtskarte',
theme: lightTheme,
darkTheme: darkTheme,
themeMode: ThemeMode.system,
initialRoute: initialRoute,
debugShowCheckedModeBanner: false,
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
Expand All @@ -53,9 +54,7 @@ class EntryWidget extends StatelessWidget {
],
supportedLocales: const [Locale('de')],
locale: const Locale('de'),
home: HomePage(
showVerification: configuration.showVerification,
),
initialRoute: initialRoute,
routes: routes,
);
} else {
Expand Down
3 changes: 2 additions & 1 deletion frontend/lib/intro_slides/intro_screen.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:ehrenamtskarte/entry_widget.dart';
import 'package:flutter/material.dart';
import 'package:intro_slider/intro_slider.dart';
import 'package:intro_slider/slide_object.dart';
Expand Down Expand Up @@ -82,7 +83,7 @@ class IntroScreenState extends State<IntroScreen> {
if (onFinishedCallback != null) {
onFinishedCallback();
}
Navigator.of(context).pop();
Navigator.of(context).pushReplacementNamed(homeRouteName);
}

@override
Expand Down
29 changes: 25 additions & 4 deletions frontend/lib/map/map/map.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import 'dart:io';
import 'dart:math' as math;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:maplibre_gl/mapbox_gl.dart';

import '../../configuration/configuration.dart';
import 'attribution_dialog.dart';
import 'map_controller.dart';
import 'screen_parent_resizer.dart';

typedef OnFeatureClickCallback = void Function(dynamic feature);
typedef OnNoFeatureClickCallback = void Function();
Expand Down Expand Up @@ -43,8 +45,9 @@ class _MapState extends State<Map> implements MapController {
MaplibreMapController? _controller;
Symbol? _symbol;
bool _permissionGiven = false;
Stack? _mapboxView;
Widget? _currentMapLibreView;
bool _isAnimating = false;
bool _isMapInitialized = false;

@override
void didChangeDependencies() {
Expand All @@ -59,13 +62,16 @@ class _MapState extends State<Map> implements MapController {
final pixelRatio = MediaQuery.of(context).devicePixelRatio;
final compassMargin = Platform.isIOS ? statusBarHeight / pixelRatio : statusBarHeight * pixelRatio;

if (_mapboxView == null || !_isAnimating) {
final Widget? currentMapLibreView = _currentMapLibreView;
final Widget maplibreView;

if (currentMapLibreView == null || !_isAnimating) {
final userLocation = widget.userLocation;
final cameraPosition = userLocation != null
? CameraPosition(target: userLocation, zoom: Map.userLocationZoomLevel)
: const CameraPosition(target: Map.centerOfBavaria, zoom: Map.bavariaZoomLevel);

_mapboxView = Stack(
maplibreView = Stack(
children: [
MaplibreMap(
initialCameraPosition: cameraPosition,
Expand Down Expand Up @@ -106,15 +112,30 @@ class _MapState extends State<Map> implements MapController {
),
],
);
} else {
maplibreView = currentMapLibreView;
}
return _mapboxView ?? Container();

// Apply ScreenParentResizer only on android
// https://github.com/flutter-mapbox-gl/maps/issues/195
return defaultTargetPlatform == TargetPlatform.android
? ScreenParentResizer(
childInitialized: _isMapInitialized,
child: maplibreView,
)
: maplibreView;
}

void _onMapCreated(MaplibreMapController controller) {
_controller = controller;
if (widget.locationAvailable) {
_controller?.updateMyLocationTrackingMode(MyLocationTrackingMode.Tracking);
}

setState(() {
_isMapInitialized = true;
});

final onMapCreated = widget.onMapCreated;
if (onMapCreated != null) {
onMapCreated(this);
Expand Down
98 changes: 98 additions & 0 deletions frontend/lib/map/map/screen_parent_resizer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import 'dart:math';
import 'package:flutter/material.dart';

/// A widget which resizes the child widget twice when the orientation changes.
///
/// * Firstly, the child put in a container with the following constraints:
/// `width = height = max(parentMaxWidth, parentMaxHeight)`. This creates a square.
/// * Secondly, the child is resized to
/// `width = parentMaxWidth, height = parentMaxHeight. This sizes the child according to the parent constraints. This
/// resize only happens if the child is already initialized. If it is not yet initialized then the resize is delayed.
class ScreenParentResizer extends StatelessWidget {
final Widget child;
final bool childInitialized;

const ScreenParentResizer({
Key? key,
required this.child,
required this.childInitialized,
}) : super(key: key);

@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return _InnerScreenParentResizer(
childInitialized: childInitialized,
constraints: constraints,
child: child,
);
},
);
}
}

class _InnerScreenParentResizer extends StatefulWidget {
final Widget? child;
final bool childInitialized;
final BoxConstraints constraints;

const _InnerScreenParentResizer({
Key? key,
required Widget this.child,
required this.childInitialized,
required this.constraints,
}) : super(key: key);

@override
State createState() => _InnerScreenParentResizerState();
}

class _InnerScreenParentResizerState extends State<_InnerScreenParentResizer> {
Orientation? _lastBuildOrientation;
Orientation? _initialOrientationUpdate;

@override
void didChangeDependencies() {
super.didChangeDependencies();

// As soon as the child is initialized we trigger the orientation update
if (widget.childInitialized) {
setState(() {
_lastBuildOrientation = _initialOrientationUpdate;
});
}
}

@override
Widget build(BuildContext context) {
final maximum = max(
widget.constraints.maxWidth,
widget.constraints.maxHeight,
);

final currentOrientation = MediaQuery.of(context).orientation;
final isOverDrawingSquare = currentOrientation != _lastBuildOrientation;

if (isOverDrawingSquare) {
// We only trigger a resize if the child component finished initializing
if (widget.childInitialized) {
setState(() {
_lastBuildOrientation = currentOrientation;
});
} else {
_initialOrientationUpdate = currentOrientation;
}
}

final width = isOverDrawingSquare ? maximum : null;
final height = isOverDrawingSquare ? maximum : null;

return OverflowBox(
alignment: Alignment.topLeft,
maxHeight: height,
maxWidth: width,
child: widget.child,
);
}
}

0 comments on commit 5f0564d

Please sign in to comment.