2022/6/10/Flutter/MaterialStateProperty

 

結論

スタート時点。TextButtonのbackgroundColorをColors.redで指定しようとするとエラーが出る。

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar:AppBar(
        //backgroundColor: Colors.red,
        backgroundColor: MaterialStateColor.resolveWith((Set<MaterialState> states){
          if(true){
            return Colors.cyanAccent;
          }
          return Colors.red;
        }),
      ),
      body: Container(
        child: Center(
          child: TextButton(
            child: Text('push'),
            onPressed: () {},
            style: ButtonStyle(
              backgroundColor:Colors.red, //error、どうすればいいの?
            ),
          ),
        ),
      ),
    );
  }
}

 

ただボタンの色を指定したい場合

(sample1)、(sample2)、いずれかの方法でできます。

//sample1
child: TextButton(
  child: Text('push'),
  onPressed: () {},
  style: TextButton.styleFrom(
    backgroundColor: Colors.red,
  ),
),

 

//sample2
child: TextButton(
  child: Text('push'),
  onPressed: () {},
  style: ButtonStyle(
    backgroundColor:MaterialStateProperty.all(Colors.greenAccent),
  ),
),

 


MaterialStateによって色を変えたい場合

デフォルトでピンク色、ホバーした時に黄色、押した時に緑色になるボタン(web,desktopのみ)

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        //backgroundColor: Colors.red,
        backgroundColor:
            MaterialStateColor.resolveWith((Set<MaterialState> states) {
          if (true) {
            return Colors.cyanAccent;
          }
          return Colors.red;
        }),
      ),
      body: Container(
        child: Center(
          child: TextButton(
            child: Text('push'),
            onPressed: () {},
            style: ButtonStyle(
              backgroundColor: MaterialStateProperty.resolveWith(
                  (Set<MaterialState> states) {
                if (states.isPressed) {
                  return Colors.green;
                } else if (states.isHovered) {
                  return Colors.yellow;
                }
                return Colors.pink;
              }),
            ),
          ),
        ),
      ),
    );
  }
}

extension MaterialStateSet on Set<MaterialState> {
  bool get isHovered => contains(MaterialState.hovered);
  bool get isPressed => contains(MaterialState.pressed);
}

 

動画で解説されていましたが、AppBarのbackgroundColorなどは、

appBar: AppBar(
  backgroundColor: Colors.red,

↑のようにColor型で指定することもできるし、上記のようにMaterialStateColor.resolveWithメソッドでの指定もできる、と、いうことですね、はい。

 


MaterialStatePropertyの訳

So there you are, working on an app, and you set out to add a button to the current page.

ここで、アプリの作業をしていて、現在のページにボタンを追加しようとしているとしましょう。

 

You plop down your button, and start thinking about the parameters you’ll need to pass to ensure it looks just right.

ボタンを配置し、見栄えをよくするために必要なパラメータを考え始めます。

 

If the date of this coding exercise was a year or two ago, you might have written code like this.

このコーディングエクササイズが 1、2年前のことだったならば、このようなコードを書いたかもしれません。

 

The button was called MaterialButton or FlatButton, and the parameters looked like this–

ボタンはMaterialButtonまたはFlatButtonと呼ばれ、パラメーターは次のようなものでした。

 

Colorと、disabledColorと、focusColorと、highlightColorです。

 

Four parameters for one attribute, depending on its interactive state.

インタラクティブなステートに応じて1つの属性に4つのパラメータがあります。

 

And we still haven’t even gotten to textColor or borderColor, each of which will require their own armada of parameters.

さらにtextColorやborderColorも追加すれば、それぞれ独自のパラメータ群が必要です。

 

And as awkward as this is, we still haven’t even reached the worst wart, to say nothing of the fact, that this problem was not limited to these three attributes or even to button.

さらに厄介なことに最悪な部分はこれからで事実は言うまでもなくこの問題は これら3つの属性や ボタンに限定されません。

 

How could Flutter know what you want if an element was both focused and hovered?

要素がfocusedであり 且つhoveredな場合、Flutterは あなたのニーズを どうやって認識するのでしょうか?

 

Sadly, it couldn’t.

それは不可能です。

 

It just had to guess.

推測する必要があります。

 

So not only have you supplied 12 color parameters, you haven’t even been able to specify exactly what you want.

色のパラメータを12個 与える必要があるだけでなく、正確なニーズを 指定することすらできていません。

 

Are we having fun yet? 

この方法でできるでしょうか?

 

OK, so this won’t do, But luckily, this entire hypothetical lives deep in the past, and it’s current year now.

はい、これではうまくいきません。しかし幸い、この架空のシナリオ全体は 遠い過去のもので、現在は進歩しました。

 

The solution first quietly arrived in Flutter as early as version 1.20, but it wasn’t implemented everywhere.

Flutterでは 早くも1.20版で 解決策が密かに提供されていましたが、実装場所は限られていました。

 

What might a potential solution look like?

では 見込みのある解決策とは?

 

One of the gaps we’ve identified is that unique combinations of these states present ambiguity.

私たちが特定したギャップの1つは、これらのステートのユニークな組み合わせが あいまいさを示すということです。

 

So you might think, OK , we’ve already written 12 parameters.

What’s a few more?

What about hovered and focused color?

こう考えるかもしれません。

「もうパラメータを12個書いたからもう少し足したら?hoveredAndFocusedな色を加えたらどう?」

 

Sadly, that idea will only take us further in the wrong direction.

残念ながら このアイデアは 間違った方向に進むだけです。

 

Instead of expanding our power by adding more parameters, we want to somehow expand it by removing parameters.

パラメータを追加し 機能を拡張する代わりに、パラメータを削除して 機能を拡張したいのです。

 

The solution the Material team came up with was to no longer accept a static value for something like backgroundColor, but instead to accept a function that returns a color.

Materialチームが考案した解決策は、背景色のようなものために静的な値を与えるのではなく、色を返す関数を与えることでした。

 

But before we can fully understand that function, we have to take a quick detour into the land of what Flutter calls MaterialStates.

この関数を完全に理解する前にFlutterのMaterialStatesをさっと見てみましょう。

 

A MaterialState is one of the various interactive states that the Material Design language recognizes.

MaterialStateは マテリアルデザイン言語が認識するさまざまなインタラクティブステートのひとつです。

 

The set includes obvious states like hovered, pressed, focused, and disabled, and other less obvious ones like scrolledUnder, which was introduced in Material3.

このセットに含まれるのはhovered、pressed、focused、 disabledなどの目に見えるステートと、scrolledUnderなどの 目に見えないものでこれはMaterial 3で導入されました。

 

In Flutter, these are implemented as a simple enum that you can look at   yourself in the material_state.dart file.

Flutterでは これらは単純な列挙型として実装され、Material_state.dartファイル内でご自分で確認することができます。

 

OK, so you’re now familiar with MaterialState, which means you’re ready to tackle those functions I mentioned earlier, because — you guessed it — they are passed the set of currently active MaterialStates.

さて、これでMaterialStateについてよく理解できたと思います。つまり、先ほど紹介した関数に取り組む準備ができたということです。なぜなら、これらの関数はその時点でアクティブなSet<MaterialState>を渡すものだからです。

 

Let’s look at some code.

コードを見ていきましょう。

 

A function that resolves these MaterialStates might look like this.

MaterialStatesを解決する関数は このようになります。

 

It accepts a set of MaterialStates, and it returns a nullable color.

その関数はSet<MaterialState>を引数として受け取り、Color?を返します。

 

This makes sense.

これが合理的です。

 

If the element is focused, it returns the FocusedColor.

要素がfocusedなら FocusedColorを返します。

 

If the element is hovered, it returns the HoveredColor.

要素がhoveredなら HoveredColorを返します。

 

And this hypothetical app has decided that these are the only two interesting states, so if neither are true, it falls back to the default.

この仮想アプリでは関連ステートはこの2つだけとします。どちらもtrueでなければ デフォルトの値を返します。

 

But what if you want to have a special color for focused and hovered?

focused 且つ hoveredな特別な色が必要な場合はどうでしょう?

 

Before, you would have been out of luck.

以前は不可能でしたが、

 

But our function can handle that.

この関数では処理できます。

 

That’s pretty good, but I will admit it’s kind of a lot of code to write.

これはかなり良いのですが、記述するコードが多いのは否めません。

 

Of course, if you need all of this struggling, then there’s no way around it.

もちろん、この苦労のすべてが必要な場合は、それを回避する方法はありません。

 

The alternative is that Flutter has to guess what you want, which is not what you want.

Flutterにニーズを推測させるのも代替案ですが、それは望ましい選択肢ではありません。

 

But what if you have a more straightforward situation?

What if you just want your button to be blue and you don’t care about anything else?

しかし、もっと簡単な状況ならどうでしょう?

ボタンを青色にしたいだけで、他に何も気にしない場合はどうなりますか?

 

Well, technically, that function could look like this.

具体的には その関数はこうなるかも知れません。

 

It’s not terrible, but it’s a lot more to type than just “Colors.blue”, which is what we used to type.

ひどいことではありませんが、それはただ「Colors.blue」と入力するよりも多くなります。

 

To find out how this API collides with real world widgets, let’s return some Material components and take a peek at their parameters.

現実世界のウィジェットとこのAPIがどう対立するかを知るため、Materialコンポーネントに戻ってパラメータを見てみましょう。

 

To start, let’s look at TextButton.

TextButtonを見てみましょう。

 

One of the new age buttons of Flutter.

Flutterで新しく登場したボタンです。

 

TextButton takes an optional style parameter, which is an instance of the ButtonStyle class.

TextButtonには オプションで styleバラメータを追加できます。これはButtonStyleクラスの インスタンスです。

 

And that ButtonStyle class looks way more organized than it used to.

このButtonStyleクラスは 従来よりはるかに簡素化されています。

 

Now for something like background color, it just takes a single parameter called backgroundColor.

背景色などにはbackgroundColorという単一パラメータを与えるだけです。

 

Great! But what’s the type of that parameters?

良いですね! しかしその引数の型は何でしょう?

 

It’s a MaterialStateProperty<Color?>?

それはMaterialStateProperty<Color?>?です。

 

which is completely self-explanatory.

これはわかりやすい名前ですね。

 

The first question is whether we can write something like this.

最初の質問は「こういうコードを書けるか?」です。

TextButton(
  style: ButtonStyle(
    backgroundColor: Colors.blue,
  ),
)

 

And sadly, we cannot.

残念ながら書けません。

 

It turns out that MaterialStateProperty of a type do not subclass that type.

つまり、MaterialStateProperty<Color>型はColor型のサブクラスではない、ということになります。

 

The code Flutter wants us to write is this.

Flutterが私たちに要求するコードはこれです。

Color? getColor(Set<MaterialState> states) => Colors.blue;

TextButton(
  style: ButtonStyle(
    backgroundColor: MaterialStateProperty.resolveWith(getColor,),
  ),
)

 

This still feels excessive for a single value, though.

これは単一の値としてはまだ(コード量が)多く感じます。

 

And luckily here, we can use the following shorthand.

そして幸いなことに、ここでは次の省略形を使用できます。

 

That .all method is handy when you just want a dead simple one-liner.

この .allメソッドは、単純なワンライナーが必要な場合に便利です。

TextButton(
  style: ButtonStyle(
    backgroundColor: MaterialStateProperty.all(Colors.blue),
  ),
)

 

But what if you don’t want any of this, and you long for the simple days of old?

しかし、これが不要で、昔の単純な日々(やり方)を待ち望んでいる場合はどうでしょうか。

 

Maybe your app will only ever target Android and iOS, so questions like hovered, focused, pressed colors are not concern,

たぶん、あなたのアプリがAndroidとiOSのみをターゲットにする場合、ホバー、フォーカス、プレスされた時の色は問題になりにくいでしょう。

 

In that case, check out the styleFrom method on the new button classes.

その場合新しいボタンクラスのstyleFromメソッドをお試しください。

 

Start with a button and supply its style parameter from the TextButton.styleFrom static method, which returns a ButtonStyle instance.

TextButtonがあって、そのstyleパラメーターに、TextButton.styleFromメソッドの返り値をセットします。

TextButton.styleFrom静的メソッドはButtonStyleインスタンスを返します。

 

And what does this method take?

このメソッドは何を受け取るんですか?

 

Simple old variables, not MaterialStateProperties.

MaterialStatePropertyではなくシンプルな以前の変数を受け取ります。

 

Colors.blue has re-entered the chat.

Colors.blueをまた使うことができましたね。

 

Now, this whole API wasn’t added to make it harder to specify a single color.

このAPIは、一つの色を指定することを難しくするために追加したのではありません。

 

It was added to make it easier, actually, possible at all– to disambiguate complex rules for when multiple MaterialStates are in play.

これは、複数のMaterialStateが当てはまるような状況で、複雑なルールを明確にすることを実際に簡単に行えるようにするために追加されました。

 


This is the scenario that really makes good use of this new API.

これこそ新しいAPIを存分に生かせるシナリオです。

 

We’ve got that function we discussed earlier passed to our MaterialStateProperty.

先ほど説明した関数(getColor)が、MaterialStatePropertyに渡されます。

 

And this is how it might actually look, too.

実際にはこのようになります。

 

This is a plausible snapshot from inside a build method.

これは、ビルドメソッド内部からのもっともらしいスナップショットです。

 

But if you’re feeling itchy at the thought of defining this closure inside your build method, you’re not alone.

でも buildメソッド内でのこのクロージャ定義で落ち着けないのはあなただけではありません。

 

So let’s take a look at some ways we might tighten this up.

これを簡潔にする方法を見てみましょう。

 

We know that whatever we pass to this parameter must be a MaterialStateProperty of the given type, so let’s start by defining a class that is exactly that.

このパラメーター(TextButtonのbackgroundColor)に渡すものはすべて、指定されたタイプのMaterialStatePropertyでなければならないことがわかっているので、それとまったく同じクラスを定義することから始めましょう。

 

I really like where this is going, but the possibility of needing to check with our theme suggests a BuildContext. So let’s add it.

これはこれで良いのですが、Themeでチェックする必要がある可能性があるため、BuildContextが必要です。そこで、それを追加してみましょう。

 

Now, I know we used the resolveWith method earlier, but that’s a shortcut for using a static version of the class.

私たちはresolveWithメソッドを使いましたが、それはstaticメソッドとして利用するためのショートカットです。

 

If we’re going to instantiate our class, then the function we have to override is resolve.

(MaterialStateProperty)クラスをインスタンス化するなら上書きする必要がある関数は resolveです。

 

And what goes in this method?

(resolve)メソッドのボディ内には何が必要でしょうか?

 

Of course, the same stuff we had before.

もちろん(resolveWithに渡したものと)同じロジックです。

 

And then back in our build method, simplicity is restored.

その後 buildメソッドに戻れば、よりすっきりしたコードになりましたよね。

 

Another detail to be aware of is a compatibility bridge that appears in several Material widgets that wanted to support this new MaterialStateProperty functionality without fully doing away with simple arguments.

もう 1 つ注意すべき点は、この新しい MaterialStateProperty 機能をシンプルな引数を排除せずにサポートしようとした、いくつかの Material ウィジェットに表示される互換性の橋渡しの部分です。

 

The most common of these is called MaterialStateColor, but there are several others.

最も一般的なものはMateralStateColorですが、他にもいくつかのものがあります。

 

AppBar backgroundColor is one such parameter of type MaterialStateColor.

AppBarのbackgroundColorはそのようなMaterialStateColor型の引数の一つです。

 

Just know that whenever you see this, you can pass either a Color or a fancy resolve.

これを見たらいつでも、Colorインスタンスか、あるいはresolveWith、どちらでも渡せます。

And one final tip to make your life easy?

最後に作業が楽になるチップの一つとして、

 

Consider adding this extension to your code base to further tighten your code.

コード量を減らすために下記のエクステンションを利用することを検討してください。

 

 

 

参考

コメントを残す

メールアドレスが公開されることはありません。