Closed69

【Dart編】Flutter入門してみた(作業ログ)

8rine238rine23

参考元のサイトに従って以下の拡張機能を導入した

- Flutter Tree
- Flutter Widget Snippets
- Awesome Flutter Snippets
- Flutter-Auto-Import
- Error Lens
- Flutter Color
8rine238rine23

ファイルをコマンドラインから実行する方法は以下みたいにできる

dart main.dart
8rine238rine23

再代入可能かどうかを判定できるメソッドとか用意されていないかな?

8rine238rine23

再代入可能かどうかを判定できるメソッドとか用意されていないかな?

dartのリファレンスチラ見したけど無いみたい

8rine238rine23

runtimeTypeで実行時の変数の型を取得できるみたいだが、これの返り値の型ってStringじゃないの?

8rine238rine23

C言語のprintf使えていたフォーマットの指定がそのまま使えそうでとっつきやすい

8rine238rine23

late修飾子なんだけど使い所があまり分からない。
変数だけ宣言しておいて、後からその変数に必ず代入する状況とは?

8rine238rine23

引数の指定がかなり柔軟にできて便利(名前付き引数とか省略可能な引数など)
引数が必須かオプションかを明示的に書けるのも便利

8rine238rine23

名前付きコンストラクタ

  • コンストラクタに名前が付けられる
  • 通常のコンストラクタとともに定義可能
  • 名前付きコンストラクタしか定義されていない場合、通常のコンストラクタでインスタンスを生成できない
  • インスタンス変数の初期化の仕方は以下みたいにできる
class Square{
  final double width;
  final double height;

  Square.zero()
    : width = 0,
      height = 0;
}
8rine238rine23

リダイレクトコンストラクタ

  • 同じクラスの別のコンストラクタを呼び出してインスタンスを生成する
class Square{
  final double width;
  final double height;

  Square(this.width, this.height);
  
  Square.createTrueSquare(double length) : this(length, length);
}
8rine238rine23

定数コンストラクタ

  • すべてのインスタンス変数にfinal修飾子が定義されているコンストラクタ
  • インスタンス変数を定数扱いできるため、キャッシュ可能になり、パフォーマンスの向上が期待できる
class Square{
  final double width;
  final double height;

  Square(this.width, this.height);
}
8rine238rine23

ファクトリコンストラクタ

  • インスタンスを返さないコンストラクタ
  • 複雑な初期化処理をファクトリコンストラクタに押し付けて、インスタンスを生成できる
  • コンストラクタの前にfactory修飾子を付けることで使用
8rine238rine23
  • イニシャライザリスト
  • コンストラクタの中でassertが使えて便利
  • スーパーコンストラクタの呼び出しやインスタンスの初期化処理もできる
8rine238rine23

インスタンスを生成する際にnewを省略できる。

final point = Point.zero();
// final point = new Point.zero();
8rine238rine23
  • 演算子のオーバーロード
  • クラスのインスタンス同士での四則演算や比較ができるようになる
  • operatorキーワード + 演算子で定義できる
class Product {
  const Product(this.amount, this.price);
  final int amount;
  final int price;
 
 Product operator +(Product product) =>
      Product(amount + product.amount, price + product.price);
}
8rine238rine23

オーバーロード可能な演算子は以下のものだけ

<,>,<=,>=,,+,/,~/,*,%,|,^,&,<<,>>,[],[]=,~,==
8rine238rine23

使う時はインスタンス同士をプリミティブな値同士を計算するように扱える(便利)

final product3 = const Product(10, 300);
final product4 = const Product(20, 500);
final total = product3 + product4;
// total.amount == 30
// total.price == 800
8rine238rine23
  • ゲッター

    • getキーワードを使用して作成
    • 引数は実装できない
  • セッター

    • setキーワードを使用して作成
    • 引数は1つだけ実装できる
class User {
  User(this._age, this.name);
  int _age;
  final String name;

  // ゲッター(メンバー変数以外も返せる)
  // 引数を実装できない
  String get expose => '$nameさんは$_age歳です';
  // セッター
  // 引数は1つだけ実装できる
  set setAge(int age) => _age = age;
}
8rine238rine23

ゲッターでメンバ変数以外を返すような実装は混乱しそうだから、
極力ゲッターではメンバ変数を返すように実装したい

8rine238rine23
  • インターフェース
  • メソッドだけが記述された抽象クラスか通常クラスをimprementimplementsすることでインターフェースを実装できる
  • 通常クラスそのものをimprementimplementsできるのは驚き
8rine238rine23
  • 継承(extends)
  • 通常のクラスも抽象クラスも継承できる
  • 継承元の変数やメソッドをオーバライドしているものには@overrideアノテーションをつける
8rine238rine23
  • overrideの型を制限(covariantキーワード)
  • 継承先のoverride時にconvariantキーワードを使うことでサブクラスに限定して書き換えができる
8rine238rine23

うまく使えば引数に誤ったクラスのインスタンスを渡すバグを減らせそう

8rine238rine23
  • noSuchMethod
  • インターフェースの実装を省略したメソッドを使おうとしたら代わりに転送されるメソッド
class ImplBase implements Base {
  // noSuchMethod
  // インターフェースの実装を省略した場合に転送されるメソッド
  
  dynamic noSuchMethod(Invocation invocation) {
    if (invocation.memberName == const Symbol('find')) {
      return [].cast<String>();
    }
    throw 'No such method';
  }
}

final test = ImplBase();
ImplBase.find();
// []
ImplBase.save();
// 例外がスローされる
8rine238rine23

これいつ使うのか分からん
(インターフェースで提供されたにもかかわらず一部だけ実装しないみたいなことってあるのか?)

8rine238rine23
  • 列挙型
  • 定数の定義をする際に便利
enum UserAccountStatus {
  Disabled,
  Enabled,
  Suspend,
}
8rine238rine23
  • 継承、Mixin、インターフェースとして実装できない
  • インスタンス化もできない
8rine238rine23
  • クラスの拡張(extension)
extension StringParsing on UserAccountStatus {
  String tailString() {
    return toString().split('.').last;
  }
}
8rine238rine23
  • ライブラリとか利用する時に、ライブラリ本体のコードは変更したくないけど、挙動をちょっとだけ変更したいときに有用かも?
  • ライブラリそのものをラップしたクラスを作れば済む話では?
8rine238rine23
  • mixin
  • クラスで再利用なコードを追加
  • phpのtraitみたいなもの
mixin Auth{
  bool_singedIn=false;
  void siginedIn(){
    _singedIn=true;
  }
  void signedOut({
    _singedIn=false;
  }
  bool get isAuthenticated=>_singedIn;}
}

class User with Auth{
  void logout(){
    signedOut();
  }
}

void main(){
  final user=User();
  // mixinで実装されているコード
  user.siginedIn();
  assert(user.isAuthenticated==true);
  // Userクラスで実装されているコード
  user.logout();
  assert(user.isAuthenticated==false);
}
8rine238rine23

Util系の処理とかmixinで追加してあげるのが主な使い道かと思う

8rine238rine23
  • 静的メソッド・変数
  • staticキーワードを付けて定義する
  • phpと使い勝手は同じ
8rine238rine23

ジェネリクス

  • 配列などの要素やインスタンス生成時に任意の型に制限すること
8rine238rine23

型を指定しないListはなんでも代入できてしまう

final anyList = ['alice', 1, []];
print(anyList); // [alice, 1, []] 
8rine238rine23

型を指定すればListの中の要素の型を指定できる

final List<String> stringList = ['alice', 'bob'];
print(stringList); // [alice, bob]
8rine238rine23

ジェネリクス型のクラスを定義して使うこともできる

class Somethig<T>{
  Somethig(this.foo);
   final T foo;
}

// インスタンスを生成するタイミングでSomethigクラスのfooの型を指定できる
final onlyInt = Somethig<int>(2);
final onlyString = Somethig<String>('alice');
8rine238rine23

ジェネリクス型を限定することができる

abstract BaseDataStore {}
class DataStore implements BaseDataStore {}
class Somethig<T extends BaseDataStore>{
  Somethig(this.foo);
  final T foo;
}

// BaseDataStore以外を指定しているためエラー
final invalid = Somethig<String>('alice');

final valid = Somethig<BaseDataStore>(DataStore());
8rine238rine23

関数にもジェネリクスを指定できる

T returnArgument<T>(T t) {
  return t;
}

final text = returnArgument<String>('Hello');
print(text); // Hello
final number = returnArgument<int>(1);
print(number); // 1
8rine238rine23

アンダースコアから始まるキーワードは、

  • 同じファイル内のクラスからはアクセス可能
  • 同じファイル外からはアクセス不可能
    になっている
8rine238rine23

これは、アンダースコアから始まるキーワードはライブラリ内だけで公開されており、
dartではファイル = ライブラリといった扱いになっているためである

8rine238rine23

参照側にpart、被参照側にpart ofディレクティブを使用することで、
別ファイルのクラスのアンダースコアから始めるキーワードにもアクセスできるようになる

8rine238rine23

外部ファイルをimportする際に、

  • 特定のクラスだけ読み込みたいときはshow
import 'class_a.dart' show Lib;
  • 特定のクラスだけ読み込みたくないときはhide
import 'class_b.dart' hide Lib;
8rine238rine23

非同期処理

  • javascriptのPromise型みたいなことができる
8rine238rine23

asyncに対するStreamの処理としての処理はaysnc for? await for?どっち?

8rine238rine23

ジェネレータ

  • Iteratable<E>を返す関数
  • sync*をつけてyieldで呼び出しもとへ値を返す
  • ジェネレータはyieldが出現するまで処理を進め、yieldで指定した値を呼び出しもとに返しジェネレータの処理を中断する
8rine238rine23
// 同期ジェネレータ
Iterable<int> naturalsTo(int n) sync* {
  var k = 0;

  while (k < n) {
    yield k++;
  }
}

void main() {
  naturalsTo(2).forEach((value) {
    print(value);
  });
8rine238rine23

非同期ジェネレータ

  • Stream<T>を返す関数
  • async*をつけてyieldで呼び出し元へ値を返す
  • 呼び出し元はlistenメソッドでyieldされた値を受け取る
8rine238rine23
// 非同期ジェネレータ
Stream<int> assynchronousNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) {
    yield k++;
  }
}

void main() {
  assynchronousNaturalsTo(10).listen((event) {
    print(event);
  });
}
8rine238rine23

再帰的ジェネレータ

  • yield*を使って別の関数へ処理を委任する
  • 委任先でyieldした結果を返す
8rine238rine23
// 再帰的ジェネレータ
Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

void main() {
  naturalsDownFrom(6).forEach((element) {
    print(element);
  });
}
8rine238rine23

call()

  • インスタンスを関数に読んだときに実行される関数を定義できる
  • callはクラスに一つしか定義できない
  • phpでいうところの__invoke()
8rine238rine23
class CallableClass{
    const CallableClass(this.username);
    final String username;

    String call(String suffix){
      return suffix + username;
    }

}

void main(){
  const username = CallableClass('alice');
  print(username('Ms.'));
}
8rine238rine23

Isorate

  • メインのイベントループのスレッドとは別にイベントループのスレッドを起動して処理を分離すること
  • スレッド間はメッセージでやり取りを行う
8rine238rine23

別のイベントループを開始するには?

  • Isolate.spwanで関数を指定する
  • 指定できる関数はトップレベル関数静的なメソッドのどちらかだけ
8rine238rine23

メッセージのやり取りはどう行うか?

  • ReceivePortのインスタンスを作成して、sendPortプロパティをIsorateに渡す
  • 送信: sendPortのsendメソッドを使う
  • 受信: ReceivePortのlistenメソッドを使う
8rine238rine23

Typedefs

  • 型や関数の定義に別名をつける
  • 再利用しやすくなり、修正が容易になる(再利用を考えていないものにつけると冗長になるため注意)
typedef SomeCallback = String Function(int i);

class Some {
  const Some(this.callback);
  final SomeCallback callback;
}
8rine238rine23

メタデータ

  • クラスやメソッドに@ではじまる任意の情報をつけられる(例:@Deprecated)
  • 静的解析などに利用される
  • メタデータはreflecteeを使って取得できる
import 'dart:mirrors';

class Schema{
  const Schema(this.table);
  final String table;
}

('users')
class DBUser{}

void main(){
  final mirror = reflect(DBUser());
  // 付与したメタデータを取得できる
  final tableName = mirror.type.metadata.first.reflectee.table;
  print(tableName);
}

8rine238rine23

Mapを要素に持つListのソート(NullSafety対応)

void main() {
  var list =  [
    {'date': '2021-10-01 23:59:59', 'body': 'body_1'},
    {'date': '2021-10-03 00:00:00', 'body': 'body_3'},
    {'date': '2021-10-04 00:00:00', 'body': 'body_4'},
    {'date': '2021-10-02 09:00:00', 'body': 'body_2'}
  ];

  print(list.runtimeType);

  // sortは破壊的変更を行うため注意
  list.sort((a, b) {
    if (a['date'] != null && b['date'] != null) {
      // a['date']とb['date']がnullではないので、null可能性を排除する
      // 降順でソートであるためcompareToの返り値にマイナスをつける
      return -a['date']!.compareTo(b['date']!);
    } else {
      return 0;
    }
  });

  print('id降順ソート');
  print(list);
  print(list[0]['body']);
}

このスクラップは2022/09/18にクローズされました