カテゴリー
App

Flutter AppにDropdownButtonを追加してみた

Flutter AppにDropdownButtonを追加してみた
Flutter AppにDropdownButtonを追加してみた
Flutter AppにDropdownButtonを追加してみた

lib/my_first_page.dart

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class MyFirstPage extends StatefulWidget {
  const MyFirstPage({Key? key}) : super(key: key);

  @override
  State<MyFirstPage> createState() => _MyFirstPageState();
}

class _MyFirstPageState extends State<MyFirstPage> {
  late TextEditingController _controller;
  bool isChecked = false;

  @override
  void initState() {
    super.initState();
    _controller = TextEditingController();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    Color getColor(Set<MaterialState> states) {
      const Set<MaterialState> interactiveStates = <MaterialState>{
        MaterialState.pressed,
        MaterialState.hovered,
        MaterialState.focused,
      };
      if (states.any(interactiveStates.contains)) {
        return Colors.blue;
      }
      return Colors.red;
    }

    String dropdownValue = 'One';

    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context)!.firstPage),
      ),
      body: Container(
        padding: const EdgeInsets.all(8),
        child: Column(
          children: <Widget>[
            Text(AppLocalizations.of(context)!.firstPage),
            TextField(
              controller: _controller,
              onSubmitted: (String value) async {
                await showDialog<void>(
                  context: context,
                  builder: (BuildContext context) {
                    return AlertDialog(
                      title: const Text('Title'),
                      content: Text(
                          '"$value", which has length ${value.characters.length}.'),
                      actions: <Widget>[
                        TextButton(
                          onPressed: () {
                            Navigator.pop(context);
                          },
                          child: const Text('OK'),
                        ),
                      ],
                    );
                  },
                );
              },
            ),
            Checkbox(
                checkColor: Colors.white,
                fillColor: MaterialStateProperty.resolveWith(getColor),
                value: isChecked,
                onChanged: (bool? value) {
                  setState(() {
                    isChecked = value!;
                  });
                }),
            DropdownButton<String>(
              value: dropdownValue,
              icon: const Icon(Icons.arrow_downward),
              iconSize: 24,
              elevation: 16,
              style: const TextStyle(color: Colors.deepPurple),
              underline: Container(
                height: 2,
                color: Colors.deepPurpleAccent,
              ),
              onChanged: (String? newValue) {
                setState(() {
                  dropdownValue = newValue!;
                });
              },
              items: <String>['One', 'Two', 'Free', 'Four']
                  .map<DropdownMenuItem<String>>((String value) {
                return DropdownMenuItem<String>(
                  value: value,
                  child: Text(value),
                );
              }).toList(),
            ),
          ],
        ),
      ),
    );
  }
}
カテゴリー
App

Flutter AppにCheckboxを追加してみた

Flutter AppにCheckboxを追加してみた
Flutter AppにCheckboxを追加してみた

lib/my_first_page.dart

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class MyFirstPage extends StatefulWidget {
  const MyFirstPage({Key? key}) : super(key: key);

  @override
  State<MyFirstPage> createState() => _MyFirstPageState();
}

class _MyFirstPageState extends State<MyFirstPage> {
  late TextEditingController _controller;
  bool isChecked = false;

  @override
  void initState() {
    super.initState();
    _controller = TextEditingController();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    Color getColor(Set<MaterialState> states) {
      const Set<MaterialState> interactiveStates = <MaterialState>{
        MaterialState.pressed,
        MaterialState.hovered,
        MaterialState.focused,
      };
      if (states.any(interactiveStates.contains)) {
        return Colors.blue;
      }
      return Colors.red;
    }

    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context)!.firstPage),
      ),
      body: Container(
        padding: const EdgeInsets.all(8),
        child: Column(
          children: <Widget>[
            Text(AppLocalizations.of(context)!.firstPage),
            TextField(
              controller: _controller,
              onSubmitted: (String value) async {
                await showDialog<void>(
                  context: context,
                  builder: (BuildContext context) {
                    return AlertDialog(
                      title: const Text('Title'),
                      content: Text(
                          '"$value", which has length ${value.characters.length}.'),
                      actions: <Widget>[
                        TextButton(
                          onPressed: () {
                            Navigator.pop(context);
                          },
                          child: const Text('OK'),
                        ),
                      ],
                    );
                  },
                );
              },
            ),
            Checkbox(
                checkColor: Colors.white,
                fillColor: MaterialStateProperty.resolveWith(getColor),
                value: isChecked,
                onChanged: (bool? value) {
                  setState(() {
                    isChecked = value!;
                  });
                }),
          ],
        ),
      ),
    );
  }
}
カテゴリー
App

Flutter AppにTextFieldを追加してみた

Flutter AppにTextFieldを追加してみた
Flutter AppにTextFieldを追加してみた

lib/my_app.dart

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:my_app_01/my_first_page.dart';
import 'package:my_app_01/my_home_page.dart';

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: DefaultTabController(
        length: 3,
        child: Scaffold(
          appBar: AppBar(
            bottom: const TabBar(
              tabs: [
                Tab(icon: Icon(Icons.looks_one)),
                Tab(icon: Icon(Icons.looks_two)),
                Tab(icon: Icon(Icons.looks_3)),
              ],
            ),
            title: const Text('Tabs Demo'),
          ),
          body: const TabBarView(
            children: [
              MyFirstPage(),
              Icon(Icons.looks_two),
              MyHomePage(title: 'Flutter Demo Home Page'),
            ],
          ),
        ),
      ),
    );
  }
}

lib/my_first_page.dart

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class MyFirstPage extends StatefulWidget {
  const MyFirstPage({Key? key}) : super(key: key);

  @override
  State<MyFirstPage> createState() => _MyFirstPageState();
}

class _MyFirstPageState extends State<MyFirstPage> {
  late TextEditingController _controller;

  @override
  void initState() {
    super.initState();
    _controller = TextEditingController();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context)!.firstPage),
      ),
      body: Container(
        padding: const EdgeInsets.all(8),
        child: Column(
          children: <Widget>[
            const Text('First Page'),
            TextField(
              controller: _controller,
              onSubmitted: (String value) async {
                await showDialog<void>(
                  context: context,
                  builder: (BuildContext context) {
                    return AlertDialog(
                      title: const Text('Title'),
                      content: Text(
                          '"$value", which has length ${value.characters.length}.'),
                      actions: <Widget>[
                        TextButton(
                          onPressed: () {
                            Navigator.pop(context);
                          },
                          child: const Text('OK'),
                        ),
                      ],
                    );
                  },
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

lib/l10n/app_en.arb

{
    "helloWorld": "Hello World!",
    "@helloWorld": {
      "description": "The conventional newborn programmer greeting"
    },
    "firstPage": "First Page","@firstPage": {"description": "First Page Title"}
}

lib/l10n/app_ja.arb

{
    "helloWorld": "こんにちは。",
    "firstPage": "最初のページ"
}
カテゴリー
App

Flutter AppにBottomNavigationBarを追加してみた

 アプリでよくある画面の下の方にタブバーを追加してみました。サンプルのまま追加しました。

Flutter AppにBottomNavigationBarを追加してみた
Flutter AppにBottomNavigationBarを追加してみた

lib/my_home_page.dart

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _selectedIndex = 0;
  static const TextStyle optionStyle =
      TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
  static const List<Widget> _widgetOptions = <Widget>[
    Text(
      'Index 0: Home',
      style: optionStyle,
    ),
    Text(
      'Index 1: Business',
      style: optionStyle,
    ),
    Text(
      'Index 2: School',
      style: optionStyle,
    ),
  ];

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  int _counter = 0;
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(AppLocalizations.of(context)!.helloWorld),
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.home),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.business),
            label: 'Business',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.school),
            label: 'School',
          ),
        ],
        currentIndex: _selectedIndex,
        selectedItemColor: Colors.amber[800],
        onTap: _onItemTapped,
      ),
    );
  }
}
カテゴリー
App

Flutter AppにTabbarを追加する

 タブで画面を切り替えられるようにTabbarを追加してみます。画面の上側に表示されるTabbarです。

Flutter AppにTabbarを追加
Flutter AppにTabbarを追加

lib/my_app.dart

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:my_app_01/my_home_page.dart';

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: DefaultTabController(
        length: 3,
        child: Scaffold(
          appBar: AppBar(
            bottom: const TabBar(
              tabs: [
                Tab(icon: Icon(Icons.looks_one)),
                Tab(icon: Icon(Icons.looks_two)),
                Tab(icon: Icon(Icons.looks_3)),
              ],
            ),
            title: const Text('Tabs Demo'),
          ),
          body: const TabBarView(
            children: [
              Icon(Icons.looks_one),
              Icon(Icons.looks_two),
              MyHomePage(title: 'Flutter Demo Home Page'),
            ],
          ),
        ),
      ),
    );
  }
}

 GoogleのMaterial Iconsは凄いですね。ダウンロードしてビルドプロジェクトにアイコン画像を登録しなくても、いきなりコード内で使用できます。アプリで使用するアイコンが全てMaterial Iconsだったら開発効率が良くなります。

カテゴリー
App

Flutter Appの多言語化

 アプリの開発途中から多言語化対応するとあちこち修正しなければならなくなるので最初から多言語化前提で開発を行います。日本語のみであったとしてもアプリとしては多言語化対応しているけれども日本語のみ設定されている状態が望ましいです。

端末の言語設定を英語にする
端末の言語設定を英語にする
画面のテキストが英語になる
画面のテキストが英語になる
端末の言語設定を日本語にする
端末の言語設定を日本語にする
画面のテキストが日本語になる
画面のテキストが日本語になる

l10n.yaml

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

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: ^0.17.0
#  intl_translation: 0.17.10+1

 intl_translation: 0.17.10+1はintl: ^0.17.0と合わないのでインストールできませんでした。

なので

return MaterialApp(
  onGenerateTitle: (BuildContext context) =>
      DemoLocalizations.of(context).title,

以降はできません。

lib/my_app.dart

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:my_app_01/my_home_page.dart';

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

lib/my_home_page.dart

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(AppLocalizations.of(context)!.helloWorld),
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

lib/l10n/app_en.arb

{
    "helloWorld": "Hello World!",
    "@helloWorld": {
      "description": "The conventional newborn programmer greeting"
    }
}

lib/l10n/app_ja.arb

{
    "helloWorld": "こんにちは。"
}
カテゴリー
App

Defaultアプリのdartファイルをクラス毎に分割する

 実際のアプリを開発するときは、起動時の処理、アプリの初期、トップ画面は別のクラスにしそのソースファイルは分割されているのでDefaultのアプリもそのように分割してみます。

main.dart

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

分割後

main.dart

import 'package:flutter/material.dart';
import 'package:my_app_01/my_app.dart';

void main() {
  runApp(const MyApp());
}

my_app.dart

import 'package:flutter/material.dart';
import 'package:my_app_01/my_home_page.dart';

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

my_home_page.dart

import 'package:flutter/material.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
カテゴリー
App

Repositoryが無くてもgit init

 ローカルだけで作業をするにしても履歴が確認できるのでプロジェクトのディレクトリでgit initとしておくと便利です。MACは標準でgitコマンドが使えます。

初めてgitを使うとき

git config --global user.email "mail@xxxxx.co.jp"
git config --global user.name "Name"

プロジェクトのディレクトリーに移動して

git init
git branch -m main
git commit --allow-empty -m "first commit"
カテゴリー
App

Flutter Default Application

 Android StudioでFlutter Projectを新規作成するとデフォルトのアプリケーションが作成されます。

Android Studio Flutter Default Application
Android Studio Flutter Default Application
Flutter Default Application for iOS
Flutter Default Application for iOS

iOSや

Flutter Default Application for Android
Flutter Default Application for Android

Androidの実機でデバック実行しFlutterでアプリを開発する環境ができているか確認します。

カテゴリー
App

Flutterのインストールで行った環境作り

.zshrc

export PATH=$PATH:/Applications/Android\ Studio.app/Contents/jre/Contents/Home/bin
export PATH=$PATH:/Users/developer/Documents/flutter/bin
export JAVA_HOME=/Applications/Android\ Studio.app/Contents/jre/Contents/Home
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"

各環境

developer@Mac-mini my_app_01 % gem -v     
3.2.22

developer@Mac-mini my_app_01 % ruby --version
ruby 3.0.2p107 (2021-07-07 revision 0db68f0233) [x86_64-darwin20]

developer@Mac-mini my_app_01 % rbenv versions                                   
  system
* 3.0.2 (set by /Users/developer/.rbenv/version)

developer@Mac-mini my_app_01 % brew -v
Homebrew 3.2.6
Homebrew/homebrew-core (git revision f909c2126a; last commit 2021-08-08)
Homebrew/homebrew-cask (git revision b4da68444e; last commit 2021-08-08)

developer@Mac-mini my_app_01 % xcode-select --version                         
xcode-select version 2384.

developer@Mac-mini my_app_01 % gem list ffi

*** LOCAL GEMS ***

ffi (1.15.3)
public_suffix (4.0.6)

developer@Mac-mini my_app_01 % xcodebuild -version
Xcode 13.0
Build version 13A5201i

developer@Mac-mini my_app_01 % pod --version
1.10.2

カテゴリー
App

Flutter DoctorでUnable to find bundled Java version.

developer@Mac-mini ~ % flutter doctor -v
[✓] Flutter (Channel master, 2.4.0-5.0.pre.166, on macOS 11.5.1 20G80
    darwin-x64, locale ja-JP)
    • Flutter version 2.4.0-5.0.pre.166 at /Users/developer/Documents/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 2dc11a85ff (2 weeks ago), 2021-07-22 17:56:03 -0700
    • Engine revision fbbb3b3f7b
    • Dart version 2.14.0 (build 2.14.0-343.0.dev)

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    • Android SDK at /Users/developer/Library/Android/sdk
    • Platform android-30, build-tools 30.0.3
    • Java binary at: /Applications/Android Studio 4.2
      Preview.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.8+10-b944.6842174)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 13.0, Build version 13A5201i
    • CocoaPods version 1.10.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio
    • Android Studio at /Applications/Android Studio 4.2 Preview.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.8+10-b944.6842174)

[!] Android Studio (version 2020.3)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    ✗ Unable to find bundled Java version.
    • Try updating or re-installing Android Studio.

[✓] VS Code (version 1.59.0)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.25.0

[✓] Connected device (2 available)
    • iPhone (mobile) • XXXXXXXX-XXXXXXXXXXXXXXXX • ios            • iOS 14.7.1
      18G82
    • Chrome (web)    • chrome                    • web-javascript • Google
      Chrome 92.0.4515.131
    ! Error: iPhone is busy: Fetching debug symbols for iPhone. Xcode will
      continue when iPhone is finished. (code -10)

! Doctor found issues in 1 category.

Unable to find bundled Java version.

が消えない。

developer@Mac-mini ~ % cd /Applications/Android\ Studio.app/Contents/jre
developer@Mac-mini jre % ln -s ../jre jdk
developer@Mac-mini jre % ln -s "/Library/Internet Plug-Ins/JavaAppletPlugin.plugin" jdk

とすると解消した。

developer@Mac-mini my_app_01 % flutter doctor -v
[✓] Flutter (Channel master, 2.4.0-5.0.pre.166, on macOS 11.5.1 20G80
    darwin-x64, locale ja-JP)
    • Flutter version 2.4.0-5.0.pre.166 at /Users/developer/Documents/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 2dc11a85ff (2 weeks ago), 2021-07-22 17:56:03 -0700
    • Engine revision fbbb3b3f7b
    • Dart version 2.14.0 (build 2.14.0-343.0.dev)

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    • Android SDK at /Users/developer/Library/Android/sdk
    • Platform android-30, build-tools 30.0.3
    • Java binary at: /Applications/Android
      Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7281165)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 13.0, Build version 13A5201i
    • CocoaPods version 1.10.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio
    • Android Studio at /Applications/Android Studio 4.2 Preview.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.8+10-b944.6842174)

[✓] Android Studio (version 2020.3)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7281165)

[✓] VS Code (version 1.59.0)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.25.0

[✓] Connected device (2 available)
    • iPhone (mobile) • XXXXXXXX-XXXXXXXXXXXXXXXX • ios            • iOS 14.7.1
      18G82
    • Chrome (web)    • chrome                    • web-javascript • Google
      Chrome 92.0.4515.131
    ! Error: iPhone is busy: Fetching debug symbols for iPhone. Xcode will
      continue when iPhone is finished. (code -10)

• No issues found!

No issues found!がやっと出た。

カテゴリー
App

FlutterでFirebase Storage

カテゴリー
App

Multiplatform2

2 評価

 どのプラットフォームを採用すれば良いかはその時々で様々な見解に分かれますが常に念頭に置いておかなければならない観点をまとめます。

2.1 ネイティブ機能へのアクセス方法

 それぞれのプラットフォームはアプリを開発出来ると謳っていますが、どこまでネイティブ固有の機能を利用できるか承知した上で利用します。開発していく中で要件を実現できないことになっては大変です。無理してできたとしても予定外のコストが掛かってしまってはそのプロジェクトを成功には導けません。

C\C++

 OS自体がC/C++でできているのでネイティブ機能は全て利用できるはずですがそのアクセス方法が利用し易い形でまとまって公開されているわけでもないので難易度が高くまた、直接OSにアクセスするのでメンテナンスが大変です。高速処理が必要なOSに依存しないビジネスロジックをSDKなどの共通ライブラリとして利用するならば良いでしょう。

Flutter

Binding to native code using dart:ffi にあるようにネイティブ機能を呼び出せます。

KMM

 Connect to platform-specific APIs にあるようにネイティブ機能を呼び出せます。

Haxe

 アプリ開発の具体的な手順があまり載っていませんのでよくわかりません。OpenFL等の高機能なライブラリを利用して実装しC++に変換してからXcodeやAndroidStudioに取り込むのでしょうか?どの程度実用性があるかは試してみないと何とも言えないです。

カテゴリー
App

Haxe

 昔のことですが随分とActionScriptにお世話になりました。だいぶ廃れてきましたがHaxeとOpenFLという形になって生き続けていると思います。実務に活かせるかどうかわかりませんが追い続けていきます。

2023.05.15
まだHaxeは生きているようです。
なので
ChatGPTに聞いてみた。

User
Haxeというアプリ開発環境は知っていますか。

ChatGPT
はい、Haxeはオープンソースのプログラミング言語およびアプリケーション開発フレームワークです。Haxeは、JavaScript、Flash、C++、Java、Python、PHP、C#、Objective-Cなど、さまざまなプラットフォームや言語で実行可能なコードを生成することができます。

Haxeは、静的型付けの言語であり、構造体やジェネリックなどの高度な機能を提供します。Haxeは、クロスプラットフォームの開発をサポートし、複数のターゲットに対してコンパイルできることが特徴です。例えば、Haxeで書かれたコードは、ウェブブラウザやモバイルデバイス上のネイティブアプリ、デスクトップアプリケーション、ゲームエンジン、およびその他のプラットフォーム上で実行できます。

Haxeには、豊富なライブラリやフレームワークが用意されており、それらを利用することで、開発者は簡単に高品質なアプリケーションを作成することができます。また、Haxeは、エディタやIDEのプラグインなど、豊富なツールセットを提供しており、効率的な開発が可能です。

Haxeは、広く利用されているプログラミング言語やフレームワークよりも新しいため、まだ一般的な知名度は高くありませんが、将来的には重要な開発環境の1つになる可能性があります。

カテゴリー
App

Multiplatform1

 今注目しているMultiplatform開発環境は、
  C/C++
  Flutter
  KMM
です。それぞれ長所、短所があるので比較してみます。それとマイナーですが個人的な思い入れで
  Haxe
も一緒に比較します。

1 概要

C/C++
 PCでもスマホでも組み込み用のマイクロコントローラーでも開発できます。Emscriptenを使えばWebアプリでも大丈夫です。古い言語なので広範囲に応用されており修得している人材の幅が広いです。

Flutter
 ビューもコントローラーもモデルもDartで開発できます。FlutterもPCでもスマホでもアプリを開発できます。デザインを統一できるアプリ開発なら共通化のメリットを存分に受けられます。

KMM
 デザインパターンでいうモデルを両モバイルOSで共通化しビューはそれぞれのIDEで作成するというコンセプトです。KMMはKotlinで作ったモデルをiOSでも使えます。OSごとの特徴を活かし且つ少しでも共通化したいアプリ開発に向いています。

Haxe
 ActionScriptから発展したマルチプラットフォーム開発環境です。Flashの資産を使えるためアニメーションを多用するならばまだ選択肢になるのではないかと思います。

カテゴリー
App

Kotlin Multiplatform Mobile

 AndroidStudio + KotlinでiOSのアプリも開発できるのがKMMです。まだ正式リリースされていませんがベータ版でその実力を見ていきましょう。

https://developer.android.com/studio/preview

 Android Studio Preview releaseをインストールします。

 ConfigureでPluginをインストールします。

 Kotlin Multiplatformをインストールします。

 Projectを新規作成しKMM Applicationを選択します。

 Androidアプリをデバック実行します。

 Androidシュミレーターで無事に動きました。

 続いてiOSアプリをデバック実行します。

 iOSシュミレーターで無事に動きました。

カテゴリー
App

Flutter


アプリをビルドできるまでXcodeをインストールします。CocoaPodsも必要です。

アプリをビルドできるまでAndroidStudioをインストールします。

AndroidStudioにDartのプラグインをインストールします。

AndroidStudioにFlutterのプラグインをインストールします。

あとは、
https://flutter.dev/docs/get-started/install/macos
の通りにインストールを行います。

flutter doctorを実行し以下のように問題が出なくなるまで設定します。

flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel master, 1.22.0-10.0.pre.142, on Mac OS X 10.15.6 19G2021,
    locale ja-JP)
 
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 11.6)
[✓] Android Studio (version 4.0)
[✓] Connected device (1 available)

• No issues found!

Androidのシミュレーターを立ち上げてからデバック実行しました。

iOSのシミュレーターを立ち上げてからデバック実行しました。

 AndroidとiOSそれぞれシュミレーターでの実行ができました。mac mini 2014 1.4GHzでは起動まで結構時間がかかるので実機をつないで検証した方がいいと思われます。