【Flutter】オブジェクトの生成

目次

概要

オブジェクトとは、いわば設計書。
トランプゲームを思い浮かべてみて欲しい。トランプだったら最低でも大体以下の情報が必要なはず

  • カードの絵柄
  • カードの数字
  • カードの画像

ジョーカーを除くとして、残りの52枚のカードの「絵柄」「数字」「画像」を一つ一つ変数を作るのはとても手間になる。
それを共通のデータの型として定義してあげれば、とてもスムーズになるはずだ!

今回、トランプのボタン全てを表示する処理を行なっているが、メインとなるのはオブジェクトの生成だけ。
View表示に関しては別途説明が必要なものが多い。

ソースコード

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  flutter_riverpod: ^1.0.0-dev.7 //ここ追加
  collection: ^1.17.1 //ここ追加

列挙体(トランプのマーク)

enum TrumpMark {
  spade,
  heart,
  dia,
  club
}

extension TrumpMarkAdditional on TrumpMark {
  String get trumpMarkString {
    switch (this) {
      case TrumpMark.spade:
        return '♠️';
      case TrumpMark.heart:
        return '❤️';
      case TrumpMark.dia:
        return '♦️';
      case TrumpMark.club:
        return '♣️';
    }
  }
}

オブジェクト生成部分

import 'TrumpMark.dart';

class TrumpCard {
  bool isSelected;
  int number;
  TrumpMark mark;

  TrumpCard(this.isSelected, this.number, this.mark);
}

アプリ起動部分

import 'package:concentration/TrumpButton/TrumpButtonView.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(ProviderScope(child: MyApp()));
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
        useMaterial3: true,
      ),
      home: const TrumpButtonView(title: 'Flutter Demo Home Page'),
    );
  }
}

View部分

import 'package:collection/collection.dart';
import 'package:concentration/Common/TrumpCard.dart';
import 'package:concentration/Common/TrumpMark.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'TrumpButtonViewModel.dart';

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

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final _viewModel = ref.watch(trumpButtonViewModelProvider);
    List<OutlinedButton> trumpButtons = [];

    for (TrumpCard trumpCard in _viewModel.trumpCards) {
      var trumpButton = OutlinedButton(
          onPressed: () { _viewModel.tapped(); },
          style: OutlinedButton.styleFrom(
              minimumSize: Size(MediaQuery.of(context).size.width/4, 40),
              backgroundColor: Colors.white10,
              foregroundColor: Colors.black,
          ),
          child: Text("${trumpCard.mark.trumpMarkString} ${trumpCard.number + 1}")
      );

      trumpButtons.add(trumpButton);
    }
    final itemsPerRow = 13;
    final slicedTrumpButtons = trumpButtons.slices(itemsPerRow).toList();

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text("TrumpButton"),
      ),
      body:
      LayoutBuilder(
        builder: (context, constraints) => SingleChildScrollView(
          physics: AlwaysScrollableScrollPhysics(),
          child: ConstrainedBox(
            constraints: BoxConstraints(minWidth: constraints.maxWidth, minHeight: constraints.maxHeight),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                for (var slicedTrumpButton in slicedTrumpButtons)
                  columnTrumpButtons(slicedTrumpButton),
              ]
            ),
          ),
        )
      )
    );
  }

  Widget columnTrumpButtons(List<OutlinedButton> trumpButtons) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        for (var trumpButton in trumpButtons)
          trumpButton,
      ]
    );
  }
}

ViewModel部分

import 'dart:ffi';

import 'package:concentration/Common/TrumpMark.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../Common/TrumpCard.dart';
import 'TrumpButtonRepository.dart';

final trumpButtonViewModelProvider = ChangeNotifierProvider((ref) => TrumpButtonViewModel(repository: ref.read(trumpButtonRepositoryRepositoryProvider)));

class TrumpButtonViewModel extends ChangeNotifier {

  TrumpButtonRepository? repository;
  List<TrumpCard> trumpCards = [];

  TrumpButtonViewModel({this.repository}){
    List<TrumpCard> trumpCards = [];
    for(TrumpMark mark in TrumpMark.values) {
      for (int num = 0; num < 13; num ++) {
        trumpCards.add(TrumpCard(false, num, mark));
      }
    }

    this.trumpCards = trumpCards;
  }

  void tapped() {
    this.repository?.tapped();
  }
}

Repository部分

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'TrumpButtonModel.dart';

final trumpButtonRepositoryRepositoryProvider = Provider((ref) => TrumpButtonRepositoryImpl(model: ref.read(trumpButtonModelModelProvider)));

abstract class TrumpButtonRepository {
  void tapped();
}

class TrumpButtonRepositoryImpl implements TrumpButtonRepository {
  TrumpButtonRepositoryImpl({required TrumpButtonModel model}): _model = model;

  final TrumpButtonModel _model;

  void tapped() {
    print("タップされたよ!");
  }
}

Model部分

import 'package:flutter_riverpod/flutter_riverpod.dart';

final trumpButtonModelModelProvider = Provider((ref) => TrumpButtonModel());

class TrumpButtonModel {
  
}

スクリーンショット

詳細

オブジェクト生成

オブジェクト生成は以下。
このように定義してあげることで、トランプのカードのデータをまとめて定義できるし、一つのオブジェクトに対して複数のデータを持たせることができる。

class TrumpCard {
  bool isSelected;
  int number;
  TrumpMark mark;

  TrumpCard(this.isSelected, this.number, this.mark);
}

以下の部分はコンストラクタと呼ばれ、オブジェクトを実体化した時に最初に呼ばれるもの。
ここでは、初期値を設定している。

TrumpCard(this.isSelected, this.number, this.mark);

インスタンス化の例は以下。
この例では、isSelectedに「false」、numberに「10」、markに「TrumpMark.spade」を格納している。

TrumpCard(false, 10, TrumpMark.spade)

説明が必要な箇所

  • 配列(List型)
  • 配列の分割
  • 列挙体とその拡張
  • 繰り返し文
  • 多重配列のデータを繰り返し文を使ってまとめて表示
  • コード要修正
  • 画面の大きさを取得する

参考ページ