The third chapter prepared an application for telemetry, automated builds, and launch presentation. Its tools and plugins are now historical; the useful structure is the separation between runtime integration, build delivery, signing, and visual assets.
Historical service workflow. App Center Build and Distribution are part of the retired service workflow. Microsoft documents separate Analytics/Diagnostics extensions in its retirement notice. The old CI steps below are not a current hosted-build setup.
The earlier telemetry integration
The source created separate Android and iOS application entries and used these Git-based plugins:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.2
appcenter:
git:
url: git://github.com/nhancv/flutter_plugin_appcenter.git
path: src/appcenter
appcenter_crashes:
git:
url: git://github.com/nhancv/flutter_plugin_appcenter.git
path: src/appcenter_crashes
appcenter_analytics:
git:
url: git://github.com/nhancv/flutter_plugin_appcenter.git
path: src/appcenter_analytics
Its application startup and demonstration screen were:
Read the historical startup example
import 'package:flutter/material.dart';
import 'package:appcenter/appcenter.dart';
import 'package:appcenter_analytics/appcenter_analytics.dart';
import 'package:appcenter_crashes/appcenter_crashes.dart';
import 'package:flutter/foundation.dart' show defaultTargetPlatform;
import 'package:flutter/foundation.dart' show TargetPlatform;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// @nhancv 10/23/2019: Config app center
final ios = defaultTargetPlatform == TargetPlatform.iOS;
String appSecret = ios
? "EXAMPLE_IOS_APP_ID"
: "EXAMPLE_ANDROID_APP_ID";
await AppCenter.start(
appSecret, [AppCenterAnalytics.id, AppCenterCrashes.id]);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CrashHome(),
);
}
}
class CrashHome extends StatefulWidget {
@override
_CrashHomeState createState() => _CrashHomeState();
}
class _CrashHomeState extends State<CrashHome> {
String _installId = 'Unknown';
bool _areAnalyticsEnabled = false, _areCrashesEnabled = false;
@override
initState() {
super.initState();
initPlatformState();
}
initPlatformState() async {
if (!mounted) return;
var installId = await AppCenter.installId;
var areAnalyticsEnabled = await AppCenterAnalytics.isEnabled;
var areCrashesEnabled = await AppCenterCrashes.isEnabled;
setState(() {
_installId = installId;
_areAnalyticsEnabled = areAnalyticsEnabled;
_areCrashesEnabled = areCrashesEnabled;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Appcenter plugin example app'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Install identifier:\n $_installId'),
Text('Analytics: $_areAnalyticsEnabled'),
Text('Crashes: $_areCrashesEnabled'),
RaisedButton(
child: Text('Generate test crash'),
onPressed: AppCenterCrashes.generateTestCrash,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('Send events'),
IconButton(
icon: Icon(Icons.map),
tooltip: 'map',
onPressed: () {
AppCenterAnalytics.trackEvent("map");
},
),
IconButton(
icon: Icon(Icons.casino),
tooltip: 'casino',
onPressed: () {
AppCenterAnalytics.trackEvent("casino", {"dollars": "10"});
},
),
],
),
],
),
);
}
}
The project identifiers have been replaced with example strings. The demo can generate a test crash and send events; no such actions were performed for this revision.
The old iOS workaround and Android permission were:
target 'Runner' do
# use_frameworks!
<uses-permission android:name="android.permission.INTERNET"/>
These describe the earlier dependencies, not universally valid fixes for later CocoaPods or SDK versions.
Keep CI and signing separate
The original CI used platform-specific appcenter-post-clone.sh scripts, repository detection, and dashboard build/distribution controls. It also cloned a debug keystore for release signing. That signing recipe is omitted: a debug credential is not a deliberate production signing identity. No keystore, signing password, build account, or distribution email was imported.
Generate the launcher assets
dev_dependencies:
flutter_launcher_icons: ^0.7.3
flutter_icons:
image_path: "assets/icons/app-icon.png"
android: true
ios: true
image_path_android: "assets/icons/app-icon.png"
image_path_ios: "assets/icons/app-icon.png"
adaptive_icon_background: "assets/icons/app-icon.png"
adaptive_icon_foreground: "assets/icons/app-icon.png"
flutter pub get
flutter pub run flutter_launcher_icons:main
The source used flutter_launcher_icons ^0.7.3; this records its declared dependency rather than a fresh compatibility check.
The earlier splash treatment
Android used a drawable and launch theme:
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<item>
<bitmap
android:gravity="center"
android:src="@drawable/ic_launcher_background" />
</item>
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
<item name= "android:windowFullscreen">true</item>
</style>
</resources>
Its embedding-specific activity variants were:
import android.os.Build
import android.os.Bundle
import android.view.ViewTreeObserver
import android.view.WindowManager
import io.flutter.app.FlutterActivity
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity : FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//make transparent status bar
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.statusBarColor = 0x00000000
}
GeneratedPluginRegistrant.registerWith(this)
//Remove full screen flag after load
val vto: ViewTreeObserver = flutterView.viewTreeObserver
vto.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
flutterView.viewTreeObserver.removeOnGlobalLayoutListener(this)
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
}
})
}
}
import android.os.Build
import android.os.Bundle
import android.view.ViewTreeObserver
import android.view.WindowManager
import io.flutter.app.FlutterActivity
import io.flutter.plugins.GeneratedPluginRegistrant
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//make transparent status bar
getWindow().setStatusBarColor(0x00000000);
GeneratedPluginRegistrant.registerWith(this);
//Remove full screen flag after load
ViewTreeObserver vto = getFlutterView().getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
getFlutterView().getViewTreeObserver().removeOnGlobalLayoutListener(this);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
});
}
}
The original iOS asset note was:
Open Runner.xcworkspace -> Runner -> Runner -> Assets.scassets -> LaunchImage -> Upload 3 type res
The source’s Java-labeled activity excerpt is preserved as supplied rather than silently represented as a verified Java build. No current application was built or launched for this revision.