🐦

Released in Flutter 3.27

2024/12/13に公開

何が変わったのか?

説明するとすごく長くなりそう。パートごとに記事を分けた方がいいのだろうと思った。パッと見てわかるところだけ書くことにした。

デフォルトのカラーがマゼンタかな。そんな感じの色になっているような。前はライトブルーな気がした。あまり重要でないかも。

こちらを見てみる。
https://docs.flutter.dev/release/breaking-changes
https://medium.com/flutter/whats-new-in-flutter-3-27-28341129570c

Xで話題になっているColumnRowspacingプロパティが追加されて、childrenの中に、複数のSizedBoxを書かなくて良くなりそう。

https://x.com/Kurogoma939/status/1867184982927753705

私もやってみたが確かに動作は同じと思う。これは便利そう。コードの可読性も上がる。

 body: Column(
        children: <Widget>[
          Text("昔のやり方"),
          Text("SizedBox 3"),
          Row(
            children: [
              Text('$_counter'),
              SizedBox(width: 10),
              Text('$_counter'),
              SizedBox(width: 10),
              Text('$_counter'),
              SizedBox(width: 10),
            ],
          ),
          Text("新しいやり方"),
          Text("spacing: 10 1個だけ"),
          Row(
            spacing: 10,
            children: [
              Text('$_counter'),
              Text('$_counter'),
              Text('$_counter'),
            ],
          ),
     ]
)

スライダーのデザインが変わったが。最近個人開発で使っていたので気づいたが区切り線があって、動きがカクカクしてたような。SwiftUIのスライダーみたいに「す〜」っと動いている。

year2023: false,は書けないな。。。見た目が良ければ気にしないのでこれは置いておこう。

https://docs.flutter.dev/release/breaking-changes/updated-material-3-slider

Slider(
            activeColor: Colors.green,
            inactiveColor: Colors.black,
            min: 0.0,
            max: 1.0,
            value: slideValue,
            onChanged: (value) {
              setState(() {
                slideValue = value;
              });
            },
          ),

Updated Material 3 Progress Indicators

LinearProgressIndicator と CircularProgressIndicator が Material 3 Design 仕様に合わせて更新されました。LinearProgressIndicator の変更点には、アクティブなトラックと非アクティブなトラックの間のギャップ、ストップインジケータ、角丸が含まれます。CircularProgressIndicator の変更には、アクティブなトラックと非アクティブなトラックの間のギャップ、丸みを帯びたストロークのキャップが含まれます。

https://docs.flutter.dev/release/breaking-changes/updated-material-3-progress-indicators

うーん。角丸あるかな。変更があったのだろう。

SizedBox(
            width: 200,
            child: LinearProgressIndicator(
              value: 0.5,
            ),
          ),
          SizedBox(height: 30),
          CircularProgressIndicator(
            value: 0.5,
          ),

Deprecate ThemeData.dialogBackgroundColor in favor of DialogThemeData.backgroundColor

ThemeData.dialogBackgroundColorを廃止し、DialogThemeData.backgroundColor`を使用する。

https://docs.flutter.dev/release/breaking-changes/deprecate-themedata-dialogbackgroundcolor

ダイアログの背景色を変更する方法が変わったようだ。

theme: ThemeData(
  dialogTheme: const DialogThemeData(backgroundColor: Colors.orange),
),

8-bit unsigned integer constructors

Constructors like Color.fromARGB remain unchanged and have continued support. To take advantage of Display P3 colors, you must use the new Color.from constructor that takes normalized floating-point color components.

8ビット符号なし整数コンストラクタ

Color.fromARGBのようなコンストラクタは変更されず、引き続きサポートされます。Display P3 カラーを利用するには、正規化された浮動小数点数カラーコンポーネントを受け取る新しい Color.from コンストラクタを使用する必要があります。

https://docs.flutter.dev/release/breaking-changes/wide-gamut-framework

Color.fromARGB -> Color.fromに変更になったようだ。

// Before
final magenta = Color.fromARGB(0xff, 0xff, 0x0, 0xff);
// After
final magenta = Color.from(alpha: 1.0, red: 1.0, green: 0.0, blue: 1.0)

色のところは難しそうと思った。ここは研究しながら学ぼう🧑‍🎓

全体のコード
main.dart
import 'package:flutter/material.dart';

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

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
        dialogTheme: const DialogThemeData(backgroundColor: Colors.orange),
        useMaterial3: false,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

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

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

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

  double slideValue = 0.0;

  // Before
  // final magenta = Color.fromARGB(0xff, 0xff, 0x0, 0xff);
  // After
  final magenta = Color.fromARGB(255, 255, 0, 255);

  final FocusNode _focusNode = FocusNode();

  
  void initState() {
    super.initState();
    debugPrint('Initial focus state: ${_focusNode.hasFocus}');
    _focusNode.addListener(_onFocusChange); // 別メソッドとして定義するのもよい
  }

  void _onFocusChange() {
    if (_focusNode.hasFocus) {
      debugPrint('フォーカスを獲得しました');
    } else {
      debugPrint('フォーカスを失いました');
    }
  }

  
  void dispose() {
    _focusNode.dispose();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: ListView(
        children: [
          Column(
            children: <Widget>[
              Text("昔のやり方"),
              Text("SizedBox 3"),
              Row(
                children: [
                  Text('$_counter'),
                  SizedBox(width: 10),
                  Text('$_counter'),
                  SizedBox(width: 10),
                  Text('$_counter'),
                  SizedBox(width: 10),
                ],
              ),
              Text("新しいやり方"),
              Text("spacing: 10 1個だけ"),
              Row(
                spacing: 10,
                children: [
                  Text('$_counter'),
                  Text('$_counter'),
                  Text('$_counter'),
                ],
              ),
              Slider(
                activeColor: Colors.green,
                inactiveColor: Colors.black,
                min: 0.0,
                max: 1.0,
                value: slideValue,
                onChanged: (value) {
                  setState(() {
                    slideValue = value;
                  });
                },
              ),
              SizedBox(height: 10),
              SizedBox(
                width: 200,
                child: LinearProgressIndicator(
                  value: 0.5,
                ),
              ),
              SizedBox(height: 30),
              CircularProgressIndicator(
                value: 0.5,
              ),
              // Button
              ElevatedButton(
                onPressed: () {
                  debugPrint('Elevated Button');
                },
                child: const Text('Elevated Button'),
              ),
              const SizedBox(height: 30),
              // Dialog Button
              TextButton(
                onPressed: () {
                  showDialog(
                    context: context,
                    builder: (context) {
                      return AlertDialog(
                        title: const Text('AlertDialog title'),
                        content: const Text('AlertDialog content'),
                        actions: <Widget>[
                          TextButton(
                            child: const Text('Cancel'),
                            onPressed: () {
                              Navigator.of(context).pop();
                            },
                          ),
                          TextButton(
                            child: const Text('OK'),
                            onPressed: () {
                              Navigator.of(context).pop();
                            },
                          ),
                        ],
                      );
                    },
                  );
                },
                child: const Text('Show Dialog'),
              ),
              const SizedBox(height: 30),
              // color box
              Container(
                width: 100,
                height: 100,
                color: magenta,
              ),
              TextField(
                focusNode: _focusNode,
                decoration: InputDecoration(
                  labelText: 'Label',
                  border: OutlineInputBorder(), // 境界線を追加
                ),
                onTap: () {
                  debugPrint('TextField tapped');
                },
              ),
            ],
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

最後に

今の所古いバージョンのプロジェクトばかり入っているので、最新バージョンのキャッチアップは自己学習だけで良さそう。試しに作ってみて昔と比べると良くなったところや新しい機能について探求してみようと思う。

新しい情報はXに投稿されていることが多いのでこちらをwatchした方が良さそう。日本語の情報もあるのでありがたい。

https://x.com/hobbydevelop/status/1867143253574979884
https://x.com/daiki1003/status/1867187467746750963
https://x.com/wes_ja0927/status/1867359795134836865
https://x.com/chooyan_i18n/status/1867033794546630835

Discussion