Dartの基本プログラム

西です。
今日はほぼ一日中プログラミングをしてました。
もうそろそろプログラミングを始めて3ヶ月くらい経ちます。
最近は本を使わず、ChatGptに教えてもらいNotionにコードをまとめる方式で勉強を進めているので、初めたての人用にと、Notionのデータが消えた時の自分用にコードをまとめとこうと思います。

メイン

import 'package:flutter/material.dart';

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

//Myappクラスの定義
class MyApp extends StatelessWidget {
  const MyApp({super.key});

//クラスの上書き
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        body: 
        Center(
        child: 
        Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children:<Widget> [
            Text('固定テキスト'),     
       ],
        ),
      ),
      )
    );
  }
}

ランダムな値

import 'dart:math';

final Random _random = Random();
//Randomクラスのインスタンス_randomを作成している

int randomNumber = _random.nextInt(7) + 1; // 1から7までのランダムな数
        print('選ばれた数: $randomNumber');

クラスとインスタンス

  • クラスは「設計図」

    • クラスは、オブジェクトの「設計図」や「型」のようなものです。クラスには属性(データ)やメソッド(処理)が定義されていますが、それ自体は実体ではありません。

  • インスタンスは「設計図から作られた具体的なモノ」

    • インスタンスは、クラス(設計図)から具体的に作られた「モノ」です。

    • クラスを元にして、そのクラスに定義された属性やメソッドを持つ実体(オブジェクト)を作り出すことを「インスタンス化」と呼びます。

//クラスの定義
class Car {
  String color;
  int speed = 0;

  Car(this.color);

  void accelerate() {
    speed += 10;
    print("スピードが $speed km/h になりました。");
  }
}
//属性:色、速度
//動作:加速する

//インスタンス化
void main() {
  Car redCar = Car("赤");  // 赤い車のインスタンス
  Car blueCar = Car("青"); // 青い車のインスタンス

  redCar.accelerate(); // 赤い車が加速
  blueCar.accelerate(); // 青い車が加速
}

MapとList

//Mapの定義
final Map<int, Widget> pages = {
  1: FirstPage(),
  2: SecondPage(),
  3: ThirdPage(),
};
//Mapの呼び出し
Widget selectedPage = pages[1]!;
//ラムダ式版
final Map<int, Function()> actions = {
  1: () => print('Action 1 executed'),
  2: () => print('Action 2 executed'),
  3: () => print('Action 3 executed'),
};

//Listの定義
List<String> myList = ['Apple', 'Banana', 'Cherry'];
//Listの呼び出し
myList[0]

関数の定義とラムダ式

//通常の関数
void printAction1() {
  print('Action 1 executed');
}

//ラムダ式による無名関数
() => print('Action 1 executed');

三項演算子

//パターン2
bool tf = false;
string onoff = '';

onoff =  tf ? 'オン' : 'オフ';
//tfがtrueならオン、falseならオフ
//tfはbool型なので比較演算子の書き方はしなくてよい

//パターン2
int num = 1;
String? next = (num == 1) ? '2' : null;

print(next); // 出力: '2'

タイマー

//呼び出されると1秒ごとに関数を呼び出す
import 'dart:async';

void main() {
  Timer.periodic(Duration(seconds: 1), (timer) {
    print("1秒ごとに呼び出される関数");

    // 例えば10回呼び出した後に停止
    if (timer.tick >= 10) {
      timer.cancel();
      print("タイマーを停止しました");
    }
  });
}

//呼び出されてから一定時間後に1度のみ関数を呼び出す
import 'dart:async';

void main() {
  Timer(Duration(seconds: 3), () {
    print("3秒後に1回だけ呼び出される関数");
  });
}

//違いはperiodicがついてるかどうか

//非同期処理
import 'dart:async';

void main() async {
  print("処理を開始しました");

  await Future.delayed(Duration(seconds: 3), () {
    print("非同期で3秒後に1回だけ呼び出される関数");
  });

  print("処理が完了しました");
}

アニメーション操作

//アニメーションスタート
void startAnimation() {
  _controller.start();
}

//アニメーションストップ
void stopAnimation() {
  _controller.stop();
}

//アニメーションリセット
void resetAnimation() {
  _controller.reset(); 
}

アニメーション具体例

import 'package:flutter/material.dart';
import 'dart:math';

//StatefulWidgetを作る
class MoveSc extends StatefulWidget{
  const MoveSc({super.key});
  @override
  _MoveScState createState() => _MoveScState();
  }

  class _MoveScState extends State<MoveSc>
  with SingleTickerProviderStateMixin{

late AnimationController _controller;
late Animation<double> _animation;

//初期化のoverride
@override
void initState(){
  super.initState();

  _controller = AnimationController(
    vsync: this,
    duration: const Duration(seconds: 10)
    );

    _animation = Tween<double>(begin: 0,end: 4*pi).animate(_controller);
}

//破棄のoverride
@override
  void dispose(){
    _controller.dispose(); //メモリ解放
    super.dispose();
  }

//スタート関数(overrideじゃない)
  void _StartAnimation(){
    _controller.reset(); //アニメーションのリセット
    _controller.forward(); //アニメーションを再生
  }

int _count = 0;
//画面のoverride
@override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        AnimatedBuilder(
          animation: _animation,
          builder: (context,child){
            double x = _animation.value * 100;
            double y = 100 * sin(_animation.value);

            return Positioned(
              right: x,
              bottom: 200 + y,
              child: ElevatedButton(
                onPressed: (){
                  setState(() {
                    _count += 1;
                  });
                },
                child: Text('$_count')
                )
              );
          }),
      ],
    );
  }
}

初期化と破棄

ページを開くたびに関数を呼び出したい場合、initStateを使う。
ただしこれはウィジェットが構築されたときのみ動作するため、ページが開くたび使いたい場合
disposeによりウィジェットを破棄する必要がある。
disposeはウィジェットが画面から削除されるたびに呼び出される。

//動的クラスの定義
class ActionPage extends StatefulWidget {
  const ActionPage({super.key});

  @override
  _ActionPageState createState() => _ActionPageState();
}

class _ActionPageState extends State<ActionPage> {

//初期化のoverride
  @override
  void initState() {
    super.initState();
    startFunction(); // ページが開いたときに関数を実行
  }
  
  //呼び出される関数
  void startFunction() {
    print("ページが開かれたので関数が開始されました");
  }
  
//破棄のoverride
  @override
  void dispose() {
    print("ページが閉じられたのでウィジェットが破棄されました");
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ActionPage'),
      ),
      body: const Center(
        child: Text('起動ページ'),
      ),
    );
  }
}

別フォルダへのアクセス

先頭にアンダーバーのついたプライベート変数は、他ファイルからアクセスできない。
よってStatefulWidgetの場合、以下のようにアクセス可能にする必要がある。
(注) 画面遷移を伴わない状況または同じ画面上での処理や画面が描画された直後に何かを行いたい場合に適しており、特定の方法でページを開いたときのみ自動で関数が呼び出したい場合には不向き

//アクセスしたいファイル側
class MoveSc extends StatefulWidget {
  const MoveSc({super.key});

  // 外部からアニメーションを開始できるメソッド
  void startAnimation(BuildContext context) {
    final state = context.findAncestorStateOfType<_MoveScState>();
    state?.StartAnimation();
  }
  //state?は「もしstateがnullでなければ」を意味する「null安全演算子」

  @override
  _MoveScState createState() => _MoveScState();
}
アクセスする側
Navigator.push(
  context,
  MaterialPageRoute(
    //ページ遷移直後に追加の処理を行うため、builderに無名関数((context) { ... })を使う
    //context(「位置情報」や「状態情報」)を引数として受け取って、{}内の処理を行う
    builder: (context) {
      //MoveScクラスのインスタンスを作成
      final moveSc = MoveSc();
      //ウィジェットの描画が完了した後に実行する処理を設定するメソッド
      WidgetsBinding.instance.addPostFrameCallback((_) {
        moveSc.startAnimation(context); // アニメーションを開始
      });
      return moveSc;
    },
  ),
);

フラグによる状態管理

別ファイルからbool型変数を渡し、その値によってinitStateなどの内容を切り替えることができる

//渡す側
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => MoveSc(shouldStartAnimation: true), // フラグをtrueにして遷移
  ),
);

//具体的には
setState(() {
        num = '';
        //インスタンス化,引数の設定
        final moveSc = MoveSc(frag: true);
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) => moveSc,
          ),
        );
      });

//複数の場合
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => MoveSc(
      shouldStartAnimation: true, // フラグ
      title: 'アニメーション画面', // 新しいパラメータ
    ),
  ),
);
//受け取る側
class MoveSc extends StatefulWidget {
  final bool shouldStartAnimation; // フラグ
  const MoveSc({super.key, this.shouldStartAnimation = false}); // デフォルトはfalse

  @override
  _MoveScState createState() => _MoveScState();
}

class _MoveScState extends State<MoveSc> with SingleTickerProviderStateMixin {

    //initState内
   //フラグがtrueのときのみアニメーションを開始
    //bool型なので比較演算子を使う必要はない
    if (widget.shouldStartAnimation) {
      _controller.forward();
    }
  }
  
  
  //複数の場合
  class MoveSc extends StatefulWidget {
  final bool shouldStartAnimation;
  final String title; // 新しい変数を追加

  const MoveSc({super.key, this.shouldStartAnimation = false, required this.title});
 //requiredは、null安全機能の一環で、パラメータが必須であることを明示(ないとエラー)

  @override
  _MoveScState createState() => _MoveScState();
}

いまのとこまとめてるのはこれだけですが、随時編集してつけたすかもしれません。
これからも勉強を続け想像を形にできるプログラマーになります!


いいなと思ったら応援しよう!