<

Flutter アプリにインタラクティブ性を追加する

ユーザー入力に反応するようにアプリを変更するにはどうすればよいでしょうか? このチュートリアルでは、アプリにインタラクティブ性を追加します。 非インタラクティブなウィジェットのみが含まれます。 具体的には、アイコンを変更してタップできるようにします 2 つのウィジェットを管理するカスタム ステートフル ウィジェットを作成することで、 ステートレスなウィジェット。

建物のレイアウトのチュートリアル作成方法を示しました 次のスクリーンショットのレイアウト。

The layout tutorial app
レイアウトチュートリアルアプリ

アプリを初めて起動すると、星が赤く点灯します。 この湖が以前にお気に入りだったことを示しています。 星の横の数字は 41 であることを示します 人々はこの湖をお気に入りにしています。このチュートリアルを完了すると、 星をタップすると、お気に入りのステータスが削除されます。 実線の星を輪郭に置き換えて、 カウントを減らします。もう一度タップすると湖がお気に入りになり、 実線の星を描いてカウントを増やします。

The custom widget you'll create

これを実現するには、単一のカスタム ウィジェットを作成します これにはスターとカウントの両方が含まれます。 それ自体がウィジェットです。星をタップすると状態が変わります 両方のウィジェットに対応しているため、同じウィジェットで両方を管理する必要があります。

すぐにコードに触れることができますステップ 2: StatefulWidget をサブクラス化する。 状態を管理するさまざまな方法を試したい場合は、 にスキップ状態の管理

ステートフル ウィジェットとステートレス ウィジェット

ウィジェットはステートフルまたはステートレスのいずれかです。ウィジェットができるなら 変化 - ユーザーが操作すると、 たとえば、ステートフルです。

ステートレスウィジェットは決して変更されません。IconIconButton、 とTextそれは ステートレス ウィジェットの例。ステートレスなウィジェット サブクラスStatelessWidget

ステートフルウィジェットは動的です: たとえば、 イベントに応じて外観を変えることができます ユーザーの操作またはデータの受信によってトリガーされます。CheckboxRadioSliderInkWellForm、 とTextFieldはステートフル ウィジェットの例です。ステートフルなウィジェット サブクラスStatefulWidget

ウィジェットの状態は次の場所に保存されます。State物体、 ウィジェットの状態を外観から分離します。 状態は、次のように変化する可能性のある値で構成されます。 スライダーの現在値、またはチェックボックスがチェックされているかどうか。 ウィジェットの状態が変化すると、 状態オブジェクトが呼び出すsetState()、 フレームワークにウィジェットを再描画するように指示します。

ステートフル ウィジェットの作成

このセクションでは、カスタム ステートフル ウィジェットを作成します。 2 つのステートレス ウィジェット (赤色の実線) を置き換えます。 星とその隣の数値 - 単一の 2 つの行を管理するカスタム ステートフル ウィジェット 子ウィジェット:IconButtonText

カスタム ステートフル ウィジェットを実装するには、次の 2 つのクラスを作成する必要があります。

  • のサブクラスStatefulWidgetウィジェットを定義します。
  • のサブクラスStateその状態が含まれています ウィジェットを作成し、ウィジェットの定義を行いますbuild()方法。

このセクションでは、ステートフル ウィジェットを構築する方法を説明します。 呼ばれたFavoriteWidget、湖アプリの場合。 セットアップ後の最初のステップは、状態を選択することです のために管理されたFavoriteWidget

ステップ 0: 準備をする

すでにアプリを構築している場合は、建物レイアウトのチュートリアル (ステップ 6)、 次のセクションに進んでください。

  1. 確認してください設定あなたの環境。
  2. 新しい Flutter アプリを作成する
  3. 交換してくださいlib/main.dartファイルmain.dart
  4. 交換してくださいpubspec.yamlファイルpubspec.yaml
  5. を作成しますimagesプロジェクト内のディレクトリを追加し、lake.jpg

デバイスを接続して有効にしたら、 または、iOSシミュレータ(Flutter インストールの一部) またはAndroidエミュレータ(Android Studio の一部 インストールしてください)、準備完了です。

ステップ 1: ウィジェットの状態を管理するオブジェクトを決定する

ウィジェットの状態はいくつかの方法で管理できます。 しかし、この例ではウィジェット自体が、FavoriteWidget、独自の状態を管理します。 この例では、星の切り替えは孤立したものです。 親ウィジェットや残りのウィジェットに影響を与えないアクション これにより、ウィジェットはその状態を内部で処理できるようになります。

ウィジェットと状態の分離について詳しくは、 そして状態はどのように管理されるのか状態の管理

ステップ 2: StatefulWidget をサブクラス化する

FavoriteWidgetクラスは自身の状態を管理し、 それでオーバーライドされますcreateState()を作成するState物体。フレームワークが呼び出すのは、createState()ウィジェットを構築したいとき。 この例では、createState()を返します のインスタンス_FavoriteWidgetState、 これは次のステップで実装します。

lib/main.dart (お気に入りウィジェット)
class FavoriteWidget extends StatefulWidget {
  const FavoriteWidget({super.key});

  @override
  State<FavoriteWidget> createState() => _FavoriteWidgetState();
}

ステップ 3: サブクラスの状態

_FavoriteWidgetStateクラスは可変データを格納します これはウィジェットの存続期間中に変更される可能性があります。 アプリを最初に起動すると、UI に固体が表示されます。 赤い星は湖が「お気に入り」のステータスであることを示します。 41のいいねとともに。これらの値は、_isFavorited_favoriteCount田畑:

lib/main.dart (_FavoriteWidgetState フィールド)
class _FavoriteWidgetState extends State<FavoriteWidget> {
  bool _isFavorited = true;
  int _favoriteCount = 41;

  // ···
}

このクラスはまた、build()方法、 これにより、赤色を含む行が作成されます。IconButton、 とText。あなたが使うIconButton(それ以外のIcon) があるからですonPressedを定義するプロパティ コールバック関数 (_toggleFavorite) タップの処理に使用します。 次にコールバック関数を定義します。

lib/main.dart (_FavoriteWidgetState ビルド)
class _FavoriteWidgetState extends State<FavoriteWidget> {
  // ···
  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        Container(
          padding: const EdgeInsets.all(0),
          child: IconButton(
            padding: const EdgeInsets.all(0),
            alignment: Alignment.centerRight,
            icon: (_isFavorited
                ? const Icon(Icons.star)
                : const Icon(Icons.star_border)),
            color: Colors.red[500],
            onPressed: _toggleFavorite,
          ),
        ),
        SizedBox(
          width: 18,
          child: SizedBox(
            child: Text('$_favoriteCount'),
          ),
        ),
      ],
    );
  }
}

_toggleFavorite()メソッド。IconButtonを押す、電話をかけるsetState()。 電話をかけるsetState()これは重要です。 ウィジェットの状態が次の状態であることをフレームワークに伝えます。 変更されたため、ウィジェットを再描画する必要があります。 関数の引数は、setState()を切り替えます これら 2 つの状態間の UI:

  • starアイコンと数字の「41」
  • star_borderアイコンと数字の 40
void _toggleFavorite() {
  setState(() {
    if (_isFavorited) {
      _favoriteCount -= 1;
      _isFavorited = false;
    } else {
      _favoriteCount += 1;
      _isFavorited = true;
    }
  });
}

ステップ 4: ステートフル ウィジェットをウィジェット ツリーにプラグインする

カスタム ステートフル ウィジェットをウィジェット ツリーに追加します。 アプリのbuild()方法。まず、次のコードを見つけます。 を作成しますIconTextを削除してください。 同じ場所にステートフル ウィジェットを作成します。

layout/lakes/{step6 → インタラクティブ}/lib/main.dart
@@ -10,2 +5,2 @@
10
5
  class MyApp extends StatelessWidget {
11
6
  const MyApp({super.key});
@@ -40,11 +35,7 @@
40
35
  ]、
41
36
  )、
42
37
  )、
43
- アイコン(
44
- アイコン.スター、
45
- 色: Colors.red[500]、
46
- )、
47
- const テキスト('41')、
38
+ const お気に入りウィジェット()、
48
39
  ]、
49
40
  )、
50
41
  );

それでおしまい!アプリをホットリロードすると、 星のアイコンがタップに反応するようになりました。

問題がありますか?

コードを実行できない場合は、 考えられるエラーについては IDE を参照してください。Flutter アプリのデバッグ役立つかもしれません。 それでも問題が見つからない場合は、 GitHub のインタラクティブな Lake サンプルとコードを比較して確認してください。

  • lib/main.dart
  • pubspec.yaml
  • lakes.jpg

ご不明な点がございましたら、開発者にお問い合わせください。コミュニティチャンネル。


このページの残りの部分では、ウィジェットの状態を変化させるいくつかの方法について説明します。 管理対象となり、他の利用可能なインタラクティブ ウィジェットをリストします。

状態の管理

ステートフル ウィジェットの状態は誰が管理するのでしょうか?ウィジェット自体? 親ウィジェット?両方?別の物体? 答えは…それは状況によります。いくつかの有効な方法があります ウィジェットをインタラクティブにします。ウィジェットデザイナーであるあなたは、 ウィジェットがどのように使用されると予想されるかに基づいて決定してください。 状態を管理する最も一般的な方法は次のとおりです。

どのアプローチを使用するかをどのように決定しますか? 次の原則は決定に役立ちます。

  • 問題の状態がユーザーデータの場合、 たとえば、チェックが入っているかどうかなど チェックボックスのモード、またはスライダーの位置、 その場合、状態は親ウィジェットによって最もよく管理されます。

  • 問題の状態が美的である場合、 たとえばアニメーションの場合、 状態はウィジェット自体によって最もよく管理されます。

疑問がある場合は、親ウィジェットで状態を管理することから始めてください。

状態を管理するさまざまな方法の例を示します 3 つの簡単な例を作成して、TapboxA、TapboxB、 そしてタップボックスC。これらの例はすべて同様に機能します。 タップすると、 緑または灰色のボックス。の_activeブール値が決定します 色: アクティブの場合は緑色、非アクティブの場合は灰色。

Active state Inactive state

これらの例では、GestureDetectorアクティビティをキャプチャする でContainer

ウィジェットは独自の状態を管理します

場合によっては、ウィジェットにとって最も意味のあることもあります 内部で状態を管理します。例えば、ListView自動的にスクロールします コンテンツがレンダー ボックスを超えています。ほとんどの開発者 を使用してListView管理したくないListViewの スクロール動作なので、ListViewそれ自体がスクロール オフセットを管理します。

_TapboxAStateクラス:

  • の状態を管理しますTapboxA
  • を定義します_activeを決定するブール値 ボックスの現在の色。
  • を定義します_handleTap()更新する関数_activeボックスがタップされて、setState()UIを更新する機能。
  • ウィジェットのすべてのインタラクティブな動作を実装します。
import 'package:flutter/material.dart';

// TapboxA manages its own state.

//------------------------- TapboxA ----------------------------------

class TapboxA extends StatefulWidget {
  const TapboxA({super.key});

  @override
  State<TapboxA> createState() => _TapboxAState();
}

class _TapboxAState extends State<TapboxA> {
  bool _active = false;

  void _handleTap() {
    setState(() {
      _active = !_active;
    });
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: Container(
        width: 200,
        height: 200,
        decoration: BoxDecoration(
          color: _active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
        child: Center(
          child: Text(
            _active ? 'Active' : 'Inactive',
            style: const TextStyle(fontSize: 32, color: Colors.white),
          ),
        ),
      ),
    );
  }
}

//------------------------- MyApp ----------------------------------

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter Demo'),
        ),
        body: const Center(
          child: TapboxA(),
        ),
      ),
    );
  }
}

親ウィジェットはウィジェットの状態を管理します

多くの場合、親ウィジェットにとってはこれが最も合理的です 状態を管理し、その子ウィジェットにいつ更新するかを指示します。 例えば、IconButton治療できるようにします タップ可能なボタンとしてのアイコン。IconButtonです ステートレス ウィジェットは親であると判断したため、 ウィジェットはボタンがタップされたかどうかを知る必要があります。 適切なアクションを実行できるようになります。

次の例では、TapboxB はその状態をエクスポートします コールバックを通じてその親に送信されます。なぜならタップボックスB 状態は管理せず、StatelessWidget をサブクラス化します。

ParentWidgetState クラス:

  • を管理します_activeTapboxB の状態。
  • 器具_handleTapboxChanged()、 ボックスがタップされたときに呼び出されるメソッド。
  • 状態が変化すると、呼び出しますsetState()UIを更新します。

TapboxB クラス:

  • すべての状態がその親によって処理されるため、StatelessWidget を拡張します。
  • タップを検知すると保護者に通知します。
import 'package:flutter/material.dart';

// ParentWidget manages the state for TapboxB.

//------------------------ ParentWidget --------------------------------

class ParentWidget extends StatefulWidget {
  const ParentWidget({super.key});

  @override
  State<ParentWidget> createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: TapboxB(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

//------------------------- TapboxB ----------------------------------

class TapboxB extends StatelessWidget {
  const TapboxB({
    super.key,
    this.active = false,
    required this.onChanged,
  });

  final bool active;
  final ValueChanged<bool> onChanged;

  void _handleTap() {
    onChanged(!active);
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: Container(
        width: 200,
        height: 200,
        decoration: BoxDecoration(
          color: active ? Colors.lightGreen[700] : Colors.grey[600],
        ),
        child: Center(
          child: Text(
            active ? 'Active' : 'Inactive',
            style: const TextStyle(fontSize: 32, color: Colors.white),
          ),
        ),
      ),
    );
  }
}

組み合わせて使用​​するアプローチ

一部のウィジェットでは、組み合わせて使用​​するアプローチにより、 一番センスがある。このシナリオでは、ステートフル ウィジェット 一部の状態と親ウィジェットを管理します 状態の他の側面を管理します。

の中にTapboxCたとえば、タップダウンすると、 ボックスの周囲に濃い緑色の境界線が表示されます。タップアップすると、 境界線が消え、ボックスの色が変わります。TapboxCそれを輸出する_active状態を親に渡しますが、その状態を管理します_highlight内部の状態。この例には 2 つありますStateオブジェクト、_ParentWidgetState_TapboxCState

_ParentWidgetState物体:

  • を管理します_active州。
  • 器具_handleTapboxChanged()、 ボックスがタップされたときに呼び出されるメソッド。
  • 電話setState()タップ時に UI を更新するには が発生し、_active状態が変化します。

_TapboxCState物体:

  • を管理します_highlight州。
  • GestureDetectorすべてのタップイベントをリッスンします。 ユーザーがタップダウンすると、ハイライトが追加されます (濃い緑色の境界線として実装されます)。ユーザーが手を離すと、 タップすると、ハイライトが削除されます。
  • 電話setState()タップダウンで UI を更新するには、 をタップするか、「キャンセル」をタップすると、_highlight状態が変化します。
  • タップ イベントで、その状態の変更を親ウィジェットに渡して取得します。 を使用した適切なアクションwidget財産。
import 'package:flutter/material.dart';

//---------------------------- ParentWidget ----------------------------

class ParentWidget extends StatefulWidget {
  const ParentWidget({super.key});

  @override
  State<ParentWidget> createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: TapboxC(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

//----------------------------- TapboxC ------------------------------

class TapboxC extends StatefulWidget {
  const TapboxC({
    super.key,
    this.active = false,
    required this.onChanged,
  });

  final bool active;
  final ValueChanged<bool> onChanged;

  @override
  State<TapboxC> createState() => _TapboxCState();
}

class _TapboxCState extends State<TapboxC> {
  bool _highlight = false;

  void _handleTapDown(TapDownDetails details) {
    setState(() {
      _highlight = true;
    });
  }

  void _handleTapUp(TapUpDetails details) {
    setState(() {
      _highlight = false;
    });
  }

  void _handleTapCancel() {
    setState(() {
      _highlight = false;
    });
  }

  void _handleTap() {
    widget.onChanged(!widget.active);
  }

  @override
  Widget build(BuildContext context) {
    // This example adds a green border on tap down.
    // On tap up, the square changes to the opposite state.
    return GestureDetector(
      onTapDown: _handleTapDown, // Handle the tap events in the order that
      onTapUp: _handleTapUp, // they occur: down, up, tap, cancel
      onTap: _handleTap,
      onTapCancel: _handleTapCancel,
      child: Container(
        width: 200,
        height: 200,
        decoration: BoxDecoration(
          color: widget.active ? Colors.lightGreen[700] : Colors.grey[600],
          border: _highlight
              ? Border.all(
                  color: Colors.teal[700]!,
                  width: 10,
                )
              : null,
        ),
        child: Center(
          child: Text(widget.active ? 'Active' : 'Inactive',
              style: const TextStyle(fontSize: 32, color: Colors.white)),
        ),
      ),
    );
  }
}

代替実装ではハイライトがエクスポートされている可能性があります アクティブな状態を内部に保持しながら状態を親に送信し、 でも誰かにそのタップボックスを使うように頼んだら、 おそらく彼らは、あまり意味がないと不満を言うでしょう。 開発者は、ボックスがアクティブかどうかを気にします。 開発者はおそらくハイライトの仕方を気にしていないでしょう 管理されており、タップ ボックスがそれらを処理することを好みます。 詳細。


その他のインタラクティブなウィジェット

Flutter は、さまざまなボタンや同様のインタラクティブなウィジェットを提供します。 これらのウィジェットのほとんどは、マテリアル デザインのガイドライン、 独自の UI を持つ一連のコンポーネントを定義します。

必要に応じて、使用できますGestureDetector構築する カスタム ウィジェットへのインタラクティブ性。 例を見つけることができますGestureDetector状態の管理。詳細については、GestureDetectorハンドルタップのレシピです。 flutterクックブック

インタラクティブ性が必要な場合は、次のいずれかを使用するのが最も簡単です。 プレハブのウィジェット。以下にリストの一部を示します。

標準ウィジェット

  • Form
  • FormField

材料成分

  • Checkbox
  • DropdownButton
  • TextButton
  • FloatingActionButton
  • IconButton
  • Radio
  • ElevatedButton
  • Slider
  • Switch
  • TextField

資力

インタラクティブ機能を追加する場合は、次のリソースが役立つ可能性があります。 あなたのアプリに。

ジェスチャーのセクション flutterクックブック

ジェスチャーの処理のセクションウィジェットの紹介
ボタンを作成して入力に応答する方法。
Flutter のジェスチャー
Flutter のジェスチャ メカニズムの説明。
Flutter API ドキュメント
すべての Flutter ライブラリのリファレンス ドキュメント。
flutterギャラリー実行中のアプリ、リポジトリ
多くのマテリアル コンポーネントと その他の flutter機能。
Flutterのレイヤードデザイン(ビデオ)
このビデオには、州に関する情報が含まれています。 ステートレスなウィジェット。 Google エンジニアのイアン ヒクソン氏が発表します。