Create First Flutter Application On Apple Silicon

In this article, we will talk about how to write your first Flutter application on Apple Silicon. One of the benefits of this post is how to structure your project on Flutter project. It help build widgets or screens consistent, or make an app easy to maintain

Before move to setup session, your environment have to meet the minimum requirements:

  • Apple silicon processor (M1 or M2)
  • Free disk space, preferably over 2.8GB
  • Git installed
  • Xcode installed
  • Visual Studio Code installed

Setup

  • Download Flutter SDK with the URL below:

https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_arm64_3.3.8-stable.zip

  • Extract previous download file
  • Add Flutter tool to your path:
export PATH="$PATH:`pwd`/flutter/bin"
  • Upgrade Flutter
flutter upgrade
  • Run Flutter Doctor
flutter doctor
  • Install Flutter/Dart plugins into VS code
  • Validate VS code with the Flutter Doctor
  1. Invoke View > Command Palette….
  2. Type “doctor”, and select the Flutter: Run Flutter Doctor.
  3. Review the output in the OUTPUT pane for any issues. Make sure to select Flutter from the dropdown in the different Output Options.
  • Create project
flutter create first_flutter_app
cd first_flutter_app
flutter run

Before writing code, we should learn how to structure your project. This ensure that the team members can follow a clear convention and add a new feature is consistent.

Project Structure

lib
+-- actions
|   +-- base
|   |   +-- base_model.dart
|   |   +-- base_service.dart
|   +-- home
|   |   +-- home_model.dart
|   |   +-- home_service.dart
+-- components
|   +-- app_bar.dart
|   +-- button.dart
+-- constants
|   +-- palette.dart
|   +-- api_end_point.dart
+-- helpers
|   +-- crud_base.dart
+-- I10n
|   +-- app_en.arb
|   +-- app_jp.arb
|   +-- support_locale.dart
|   +-- base.dart
+-- providers
|   +-- local_provider.dart
+-- reducers
|   +-- app_state.dart
|   +-- reducer.dart
|   +-- selectors.dart
+-- saga
|   +-- index.dart
+-- screen
|   +-- home
|   |   +-- home_screen.dart
|   |   +-- home_view_model.dart
|   |   +-- home.dart
+-- utilities
+-- app.dart
+-- I10n.yaml
+-- main.dart

If you have knowledge about OOP and basic programming concept. Move on writing code on Flutter now.

Writing You First App

pubspec.yaml

name: first_flutter_app
description: A new Flutter Project

publish_to: none

version: 1.0.0+1

environment:
  sdk: ">=2.17.6 <3.0.0"
  
dependencies
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  
  cupertino_icon: ^1.0.2
  get: ^4.6.5
  http: ^0.13.5
  redux: ^5.0.0
  redux_saga: ^3.0.6
  flutter_redux: ^0.10.0
  intl: ^0.17.0
  loading_gifs: ^0.3.0
  localstorage: ^4.0.0+1
  provider: ^5.0.0
  timeline_tile: ^2.0.0
  
dev_dependencies:
  flutter_test:
    sdk: flutter
  
  flutter_lints: ^2.0.0
  
flutter:
  uses-material-design: true
  generate: true
  assets:
    - assets/images/

analysis_options.yaml

include: package:flutter_lints/flutter.yaml

linter:
  rules:
    prefer_single_quotes: true

main.dart

Store<AppStore> configureStore() {
  SagaMiddleware sagaMiddleware = createSagaMiddleware();
  
  final store = Store<AppState>(
    appReducer,
    initialState: AppState.initial(),
    middleware: [
      applyMiddleware(sagaMiddleware),
    ],
  );
  
  sagaMiddleware.setStore(store);
  sagaMiddleware.run(rootSaga);
  
  return store;
}

void main() sync {
  Store<AppState> store = configureStore();
  
  await store.dispatch(GetAccessTokenByAccessTokenRequest());
  
  runApp(App(store: store));
}

app.dart

class App extends StatelessWidget {
  final Store<AppState> store;
  
  App({required this.store}) : super();
  
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => LocaleProvider(),
      child: Consumer<LocaleProvider>(
        builder: (context, localeProvider, child) => StoreProvider<AppState>(
          store: store,
          child: MateialApp(
           title: "First Flutter App",
           them: ThemData(
             appBarTheme: AppBarTheme(
               color: Palette.primary,
             )
           ),
           localizationsDelegates: [
             AppLocalizations.delegate,
             GlobalWidgetsLocalizations.delegate,
             GlobalCupertinoLocalizations.delegate
           ],
           locale: localeProvider.locale,
           supportedLocales: L10n.support,
           initialRouter: '/',
           routes: {
             '/': (context) => const Home(),
           },
           onUnknownRoute: (RouteSettings setting) {
             return MaterialPageRoute(builder: (context) => NotFound());
           }
          )
        )
      )
    )
  }
}

home_screen.dart

class HomeScreen extends State<Home> {
  @override
  void initState() {
    super.initState();
  }
  
  @override
  Widget build(BuildContext context) {
    return StoreConnector<AppState, HomeViewModel>(
      converter: HomeViewModel.fromStore,
      builder: (context, vm) {
        return MaterialApp(
          title: 'Welcome to Flutter',
          home: Scaffold(
            appBar: AppBar(
              title: const Text('Welcome to Flutter'),
            ),
            body: const Center(
              child: Text('Hello World'),
            ),
          ),
        );
      }
    )
  }
}

home_view_model.dart

class HomeViewModel {
  HomeViewModel();
  
  static HomeViewModel fromStore(Store<AppState> store) {
    return HomeViewModel();
  }
}

home.dart

class Home extends StatefulWidget {
  const Home({Key? key}): super(key: key);
  
  @override
  State<Home> createState() => HomeScreen();
}

saga/index.dart

rootSaga() sync* {
}

app_state.dart

@immutable
class AppState {
  const AppState({});
  
  factory AppState.initial() {
    return AppState();
  }
}

reducer.dart

AppState appReducer(AppState state, action) {
  return AppState();
}

I10n.yaml

arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart

app_en.arb

{
  "hello": "Hello"
}

app_jp.arb

{
  "hello": "こんにちは"
}

locale_provider.dart

class LocaleProvider with ChangeNotifier {
  Locale? _locale;
  Locale? get locale => _locale;
  
  void setLocale(Locale loc) {
    if (!L10n.support.contains(loc)) return;
    
    _locale = loc;
    notifyListeners();
  }
  
  void clearLocale() {
    _locale = null;
    notifyListeners();
  }
}

support_locale.dart

class L10n {
  static const List<Locale> support = [Locale("en"), Locale("jp")];
}

palette.dart

class Palette {
  static const Color primary = Color(0xFF004FC7);
}

Running Flutter App

Flutter has many different build mode to run your application: Debug, Profile, Release. You should write configuration on .vscode/launch.json

{
  "version": "0.2.0",
  "configuration": [
    {
      "name": "First Flutter App",
      "cwd": "Flutter/first_flutter_app",
      "request": "launch",
      "type": "Dart"
    },
    {
      "name": "First Flutter App (profile mode)",
      "cwd": "Flutter/first_flutter_app",
      "request": "launch",
      "type": "Dart",
      "flutterMode": "profile"
    },
    {
      "name": "First Flutter App (release mode)",
      "cwd": "Flutter/first_flutter_app",
      "request": "launch",
      "type": "Dart",
      "flutterMode": "release"
    }
  ]
}

Then, run application from supported VS code. Your application should be start with hot reload that feature feature helps you quickly and easily experiment, build UIs, add features, and fix bugs.

  • Hot reload: ^F5
  • Hot restart: ⇧⌘F5
  • Full restart: There is no specific keyboard shortcut for this; you need to stop and start the run configuration.

That's it, thank you for reading!