<Flutter Dart>String interpolation(変数の文字列展開)

String interpolation(変数の文字列展開)

変数を文字列に展開

String name = 'Bob';
print('My name is $name');

// output
// My name is Bob

変数をくくる

変数の後に続けてアルファベットを入力するとエラーとなる

String name = 'Bob';
print('My name is $nameA');

// output
// Error: Undefined name 'nameA'.
// print('My name is $nameA');

変数の後に続けてアルファベットを入力する場合は 変数名を中括弧「{」「}」で囲む。

String name = 'Bob';
print('My name is ${name}A');

// output
// My name is BobA

文字列ないで変数の計算

文字列内でロジックを書く場合、

print('I am ${28 + 1} years old.');  // 変数は含めずにロジックを書くことができる

int age = 27;
print('I am ${age + 1} years old.');  // 変数を含めてロジックを書くことができる

// output
// I am 28 years old.
// I am 29 years old.

パッケージの導入時のpubspec.yamlの書き方

はじめに

パッケージ導入する時にはpubspec.yamldependencyに追記します。 pub.devから取得する以外にも方法があります。 その方法をメモしておきます。

  • pub.devからパッケージ取得
  • gitのリポジトリからパッケージ取得
  • ローカルからパッケージ取得

riverpodの取得を例にします。

riverpod | Dart Package

pub.devからパッケージ取得

dependencies:
  riverpod: ^2.3.6

gitのリポジトリからパッケージ取得

dependencies:
  riverpod: 
    git: 
      url: https://github.com/rrousselGit/riverpod.git
      path: packages/riverpod

ローカルからパッケージ取得

下記のpathにgithubからriverpodを取得しておきます。 その上で下記の通り記載します

dependencies:
  riverpod:
    path: /Users/example/Git/riverpod/packages/riverpod

最後に

詳しく知りたい場合はこちらを参照

Package dependencies | Dart

アプリのアイコン画像を設定する

はじめに

アプリをリリースしようとするときに必要になります。 個人的にはリリース前でもアイコンを設定しておくとモチベーションがあがります。

手順

イコン画像(1024px)は既にある前提です。 用意してあるアイコン画像をプロジェクト配下のフォルダに保存しておきます。 下記手順ではassets/icon/icon.pngに保存している前提です。

その前提で下記の手順が必要となります。

  1. flutter_launcher_iconsのパッケージ導入
  2. pubspec.ymlに設定
  3. アイコン作成処理実行

1. flutter_launcher_iconsのパッケージ導入

こちらがパッケージの公式ページです。

flutter_launcher_icons | Dart Package

pubspec.ymlのdev_dependenciesにflutter_launcher_iconsの行を追加し、 flutter pub getコマンドでパッケージ取得します。

dev_dependencies:
  flutter_launcher_icons: "^0.13.1" # 追加

2. pubspec.ymlに設定

flutter_launcher_icons:
 android: true
 ios: true
 remove_alpha_ios: true # アイコン画像にアルファが設定されている場合に追加
 image_path: "assets/icon/icon.png" # 素材のアイコン画像が保存されているパス

3. アイコン作成処理実行

下記コマンド実行します。 アプリに必要な各種サイズのアイコン画像が作成され、iOSAndroidのプロジェクトにアイコンが設定されます。 実行後、アプリをビルドしてアイコン画像が設定されていることを確認します。

 dart run flutter_launcher_icons

MaterialApp.routerのrouterConfig

はじめに

画面遷移でgo_routerを使っていて、initialLocationを設定しても効いていないことがあった。 原因はMaterialApp.routerの引数にrouteInformationProviderをセットし忘れていたからだった。

原因を調べていたときにFlutter公式では下記のようにrouterConfigに GoRouterオブジェクトをセットしていて、routerDelegateやrouteInformationParserなどを使っていない。

MaterialApp.router(
  routerConfig: GoRouter(
    // …
  )
);

画面遷移でgo_routerを使い始めたときにはなかった方法 or 勉強に利用したサイトがそういうやりかたをしていたのだろう。 routerDelegateやrouteInformationParserなどをセットしなくてもいいやり方があるのかと思い、 routerConfigについて少し調べたのでそのメモを残す。

RouterConfigについて

RouterConfigの定義は下記のとおり。

/// A convenient bundle to configure a [Router] widget.
///
/// To configure a [Router] widget, one needs to provide several delegates,
/// [RouteInformationProvider], [RouteInformationParser], [RouterDelegate],
/// and [BackButtonDispatcher]. This abstract class provides way to bundle these
/// delegates into a single object to configure a [Router].
///
/// The [routerDelegate] must not be null. The [backButtonDispatcher],
/// [routeInformationProvider], and [routeInformationProvider] are optional.
///
/// The [routeInformationProvider] and [routeInformationParser] must
/// both be provided or not provided.
class RouterConfig<T> {
  /// Creates a [RouterConfig].
  ///
  /// The [routerDelegate] must not be null. The [backButtonDispatcher],
  /// [routeInformationProvider], and [routeInformationParser] are optional.
  ///
  /// The [routeInformationProvider] and [routeInformationParser] must
  /// both be provided or not provided.
  const RouterConfig({
    this.routeInformationProvider,
    this.routeInformationParser,
    required this.routerDelegate,
    this.backButtonDispatcher,
  }) : assert((routeInformationProvider == null) == (routeInformationParser == null));

  /// The [RouteInformationProvider] that is used to configure the [Router].
  final RouteInformationProvider? routeInformationProvider;

  /// The [RouteInformationParser] that is used to configure the [Router].
  final RouteInformationParser<T>? routeInformationParser;

  /// The [RouterDelegate] that is used to configure the [Router].
  final RouterDelegate<T> routerDelegate;

  /// The [BackButtonDispatcher] that is used to configure the [Router].
  final BackButtonDispatcher? backButtonDispatcher;
}

A convenient bundle to configure a [Router] widget.

コメントの一文が示している通り、まとめて設定してくれる。 まとめて設定してくれる対象は下記のとおりでした。

  • routerDelegate
  • backButtonDispatcher
  • routeInformationProvider
  • routeInformationProvider

セグメントボタン(SegmentedButton)を使った実装

はじめに

セグメントボタンの使い道としては 複数の選択肢から選択し、

  • ビューを切り替える
  • ソート順を切り替える

など、が挙げられます。

SegmentedButtonクラス

# コンストラクタ
SegmentedButton({
  Key? key,
  required List<ButtonSegment<T>> segments,
  required Set<T> selected,
  void onSelectionChanged(Set<T>)?,
  bool multiSelectionEnabled = false,
  bool emptySelectionAllowed = false,
  ButtonStyle? style,
  bool showSelectedIcon = true,
  Widget? selectedIcon
})
  • segmentsが選択肢です
  • segmentsの各要素であるButtonSegmentのvalueが選択されたときに取得できる値です
  • onSelectionChangedが選択されたときに実行される処理です

実装例

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SegmentedButton'),
      ),
      body: Container(
        color: Colors.grey,
        child: Center(
          child: SegmentedButton(
            segments: const <ButtonSegment<int>>[
              ButtonSegment(
                value: 0,
                label: Padding(
                  padding: EdgeInsets.all(4.0),
                  child: Text('年'),
                ),
              ),
              ButtonSegment(
                value: 1,
                label: Text('月'),
              ),
            ],
            selected: const <int>{ 0 },
            onSelectionChanged: (Set<int> newSelection) => print('$newSelection'),
          ),
        ),
      )
    );
  }
}

公式ドキュメント

api.flutter.dev

iOSのWireless debugging(Wifi経由での実機デバッグ)

はじめに

実機で動かすとモチベーションがあがるタイプです。 ときどきケーブルが無くて、仕方なくシミュレータでデバッグしてました。 Flutter3.10.0でWifi経由で実機にアプリ転送し、デバッグできるようになったようです。

iOSのWireless debugging設定

Flutter3.10.0以上でないとできないので、FlutterがFlutter3.10.0以上であることを確認しましょう。 そして、「1. iOS端末へのアプリ転送設定」はケーブルを使った有線であっても必要になる設定です。すでに設定済みであればスキップして「2. Wireless debugging設定」をします。これでWifi経由で実機でデバッグできるようになりました!

1. iOS端末へのアプリ転送設定

  1. 開発MaciOS端末をケーブルでつなぐ
  2. もしiOS16以上であれば、設定アプリの「プライバシーとセキュリティ」のデベロッパーモードをオンにする
  3. 開発中のFlutterアプリのRunner.xcworkspaceをXcodeで開く
  4. Signing & Capabilities > Teamでアカウントを設定する
  5. automatically manage signingにチェックが入っていない場合は、General > Identity > Bundle Identifierを設定する
  6. Xcodeで設定したiOS端末を選択してアプリ起動する or flutter runする

2. Wireless debugging設定

  1. 開発MaciOS端末で同じWifiネットワークにつなぐ
  2. 開発中のFlutterアプリのRunner.xcworkspaceをXcodeで開く
  3. Xcodeを起動し、上部バーからXcode > Window > Devices and Simulatorsを開く。
  4. 接続しているiOS端末を探し、「Connect via Network」にチェックを入れる。しばらく処理中のプログレスが出ることがあるが終わったらケーブルをはずしても大丈夫です
  5. Xcode > Product > Scheme > Edit Schemeを開く

  6. Argumentsを開く

  7. IPv4ネットワークの場合、--vm-service-host=0.0.0.0を追加(IPv6の場合は--vm-service-host=::0)をArguments Passed On Launchに追加する
  8. Xcodeで設定したiOS端末を選択してアプリ起動する or flutter runする

[Flutter]Dart3で追加されたClass Modifier

はじめに

Google I/O 2023でDart3のリリースが報告されました。 Dart3でClass Modifierが追加されたので試してみました。 (Class Modifierとは、抽象Classの頭につけているabstractなどです)

interfaceクラス

// vehicle.dart
interface class Vehicle {
  void drive() {
    print('drive');
  }
}

このVehicleクラスが定義されたファイルとは別ファイルで使う場合に下記の特長があります。

  1. インスタンス化できる
  2. 継承できない
  3. implementsできる
// car.dart
import 'vehicle.dart';

// インスタンス化できる
final myCar = Vehicle();

// 継承できない(エラーになる)
class Car extends Vehicle {
  String name;
  Car({
    required this.name,
  }) : super();
}

// implementsできる
class Truck implements Vehicle {
  String name;
  Truck({
    required this.name,
  }) : super();

  @override
  void drive() {
    print('drive truck');
  }
}

baseクラス

// vehicle.dart
base class Vehicle {
  void drive() {
    print('drive');
  }
}

このVehicleクラスが定義されたファイルとは別ファイルで使う場合に下記の特長があります。

  1. インスタンス化できる
  2. 継承できる。ただし、base or finalのクラスに限る
  3. implementsできない
// car.dart
import 'vehicle.dart';

// インスタンス化できる
final myCar = Vehicle();

// 継承できる。base or finalでなければならない
base class Car extends Vehicle {
  String name;
  Car({
    required this.name,
  }) : super();
}

final class Bus extends Vehicle { }

// implementsできない
base class Truck implements Vehicle {
  String name;
  Truck({
    required this.name,
  }) : super();

  @override
  void drive() {
    print('drive truck');
  }
}

finalクラス

// vehicle.dart
final class Vehicle {
  void drive() {
    print('drive');
  }
}

このVehicleクラスが定義されたファイルとは別ファイルで使う場合に下記の特長があります。

  1. インスタンス化できる
  2. 継承できない
  3. implementsできない
// car.dart
import 'vehicle.dart';

// インスタンス化できる
final myCar = Vehicle();

// 継承できない
base class Car extends Vehicle {
  String name;
  Car({
    required this.name,
  }) : super();
}

// implementsできない
base class Truck implements Vehicle {
  String name;
  Truck({
    required this.name,
  }) : super();

  @override
  void drive() {
    print('drive truck');
  }
}