Map Update ¶
|
|||
Setup ¶
Prerequisites ¶
Build and Run ¶
Start a terminal/command prompt and navigate to the
map_update
directory within the Flutter examples directory. This is the name of the example project.
Note - the
gem_kit
directory containing the Maps SDK for Flutter
should be in the
plugins
directory of the example, e.g.
example_pathname/plugins/gem_kit
- see the environment setup guide above.
Run:
flutter
pub
get
Configure the native parts:
First, verify that the
ANDROID_SDK_ROOT
environment variable
is set to the root path of your android SDK.
In
android/build.gradle
add the
maven
block as shown,
within the
allprojects
block, for both debug and release builds:
allprojects {
repositories {
google()
mavenCentral()
maven {
url "${rootDir}/../plugins/gem_kit/android/build"
}
}
}
in
android/app/build.gradle
within the
android
block, in the
defaultConfig
block,
the android SDK version
minSdk
must be set as shown below.
Additionally, for release builds, in
android/app/build.gradle
,
within the
android
block, add the
buildTypes
block as shown:
Replace
example_pathname
with the actual project pathname
android {
defaultConfig {
applicationId "com.magiclane.gem_kit.examples.example_pathname"
minSdk 21
targetSdk flutter.targetSdk
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
minifyEnabled false
shrinkResources false
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
Then run the project:
flutter run --debug
orflutter run --release
App entry and initialization ¶
const projectApiToken = String.fromEnvironment('GEM_TOKEN');
void main() {
runApp(const MyApp());
}
This code initializes the projectApiToken with the required authorization token and launches the app.
How it Works ¶
This example demonstrates the following features:
-
Replace the default map with an older version from the app’s assets to simulate a map update.
-
Apply the necessary logic to update the map to the latest version.
-
Manage and integrate map update functionality seamlessly within the app’s UI.
UI and Map Integration ¶
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Map Update',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
|
|
|
|
Handle the map update logic and UI ¶
Within
_MyHomePageState
, define the necessary state variables and methods to interact with the map and manage updates.
class _MyHomePageState extends State<MyHomePage> {
int? mapId;
void onMapCreated(GemMapController controller) async {
mapId = controller.mapId;
}
@override
void dispose() {
GemKit.release();
super.dispose();
}
bool showButton = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepPurple[900],
title: const Text(
'Map Update',
style: TextStyle(color: Colors.white),
),
actions: [
IconButton(
onPressed: () => _onMapButtonTap(context),
icon: const Icon(
Icons.map_outlined,
color: Colors.white,
))
],
),
body: GemMap(
onMapCreated: onMapCreated,
),
floatingActionButton: showButton
? FloatingActionButton(
onPressed: loadMaps,
child: const Icon(Icons.file_copy),
)
: null,
);
}
// Method to navigate to the Maps Page.
void _onMapButtonTap(BuildContext context) async {
if (mapId != null) {
Navigator.of(context).push(MaterialPageRoute<dynamic>(
builder: (context) => MapsPage(mapId: mapId!),
));
}
}
Future<bool> loadAsset(String assetName, String destinationDirectoryPath) async {
final destinationFilePath = path.join(destinationDirectoryPath, assetName);
File file = File(destinationFilePath);
if (await file.exists()) {
return false;
}
await file.create();
final asset = await rootBundle.load('assets/$assetName');
final buffer = asset.buffer;
await file.writeAsBytes(buffer.asUint8List(asset.offsetInBytes, asset.lengthInBytes), flush: true);
print("Wrote file ${file.path}");
return true;
}
Future<void> loadMaps() async {
const cmap = 'AndorraOSM_2021Q1.cmap';
const worldMap = 'WM_7_406.map';
final dirPath = await getDirPath();
final resFilePath = path.joinAll([dirPath.path, "Data", "Res"]);
final mapsFilePath = path.joinAll([dirPath.path, "Data", "Maps"]);
await deleteAssets(resFilePath, RegExp(r'WM_\d_\d+\.map'));
await deleteAssets(mapsFilePath, RegExp(r'.+\.cmap'));
await loadAsset(cmap, mapsFilePath);
await loadAsset(worldMap, resFilePath);
ContentStore.refreshContentStore();
setState(() {
showButton = false;
});
}
Future<Directory> getDirPath() async {
if (Platform.isAndroid) {
return (await getExternalStorageDirectory())!;
} else if (Platform.isIOS) {
return await getApplicationDocumentsDirectory();
} else {
throw Exception('Platform not supported');
}
}
Future<void> deleteAssets(String directoryPath, RegExp pattern) async {
final directory = Directory(directoryPath);
for (final file in directory.listSync()) {
final filename = path.basename(file.path);
if (pattern.hasMatch(filename)) {
try {
print('INFO DELETE ASSETS: deleting file ${file.path}');
file.deleteSync();
} catch (e) {
print('\x1B[31mWARNING: Deleting file ${file.path} failed. Reason:\n${e.toString()}\x1B[0m');
}
}
}
}
}