[Newbie] Chapter 3. Flutter initial setup

[Newbie] Chapter 1. Flutter overview

[Newbie] Chapter 2. Flutter getting started

Content

  • Config to App Center
  • CI config
  • App splash screen
  • App launcher
  • App version and env

Config to App Center

  • With Flutter, you need to create two apps separately for android and ios
  • Update dependencies at pubspec.yaml
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
  • Modify lib/main.dart to integrate App center
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
      ? "4cb4478e-9803-92cf-02ef-00f272f9768d"
      : "7bf557b6-6739-028f-b83e-ba192d3f7972";
  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"});
                },
              ),
            ],
          ),
        ],
      ),
    );
  }
}
  • For iOS, you may face with pod install error, just make a little change at ios/Podfile: put comment # on use_frameworks! line, then run it again.
target 'Runner' do
  # use_frameworks!
  • For Android, add internet permission to android/app/src/main/AndroidManifest.xml
    <uses-permission android:name="android.permission.INTERNET"/>
  • Run app
  • Click on ‘Generate test crash’ and go to App center to check crash report

CI config


    signingConfigs {
        debug {
            keyAlias 'androiddebugkey'
            keyPassword 'android'
            storeFile file("keystores/keystore-debug.jks")
            storePassword 'android'
        }
        release {
            keyAlias 'androiddebugkey'
            keyPassword 'android'
            storeFile file("keystores/keystore-release.jks")
            storePassword 'android'
        }
    }

    buildTypes {
        debug {
            debuggable true
            signingConfig signingConfigs.debug
        }
        release {
            debuggable false
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
  • Commit these script to remote
  • Go to Appcenter dashboard -> Build -> Connect to your source -> Config -> Save & Build
  • Wait for building
  • After build successful, you can Download or Distribute it
  • iOS build status
  • After distributed, collaborators will get an email

App launcher

  • Prepare app icon: https://jgilfelt.github.io 
  • Get the biggest icon, rename it to app-icon.png and place it to assets/icons/app-icon.png
  • Update pubspec.yaml
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"
  • Generate icon
flutter pub get
flutter pub run flutter_launcher_icons:main
  • Stop and Run the app again

App splash screen

Android

  • Update android\app\src\main\res\drawable\launch_background.xml (remember using the resource in drawable, not in mipmap)
<?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>
  • Update android\app\src\main\res\values\styles.xml
<?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>
  • Update android native MainActivity at src\main\...

Kotlin

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)
            }
        })
    }
}

Java

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);
            }
        });
    }
}

iOS

Open Runner.xcworkspace -> Runner -> Runner -> Assets.scassets -> LaunchImage -> Upload 3 type res

Leave a Reply

Your email address will not be published.Required fields are marked *