Movatterモバイル変換


[0]ホーム

URL:


Skip to main content

docs.flutter.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic.

Learn more

Flutter 3.41 is live! Check out theFlutter 3.41 blog post!

Add interactivity to your Flutter app

How to implement a stateful widget that responds to taps.

What you'll learn
  • How to respond to taps.
  • How to create a custom widget.
  • The difference between stateless and stateful widgets.

How do you modify your app to make it react to user input? In this tutorial, you'll add interactivity to an app that contains only non-interactive widgets. Specifically, you'll modify an icon to make it tappable by creating a custom stateful widget that manages two stateless widgets.

Thebuilding layouts tutorial showed you how to create the layout for the following screenshot.

The layout tutorial app

The layout tutorial app

When the app first launches, the star is solid red, indicating that this lake has previously been favorited. The number next to the star indicates that 41 people have favorited this lake. After completing this tutorial, tapping the star removes its favorited status, replacing the solid star with an outline and decreasing the count. Tapping again favorites the lake, drawing a solid star and increasing the count.

The custom widget you'll create

To accomplish this, you'll create a single custom widget that includes both the star and the count, which are themselves widgets. Tapping the star changes state for both widgets, so the same widget should manage both.

You can get right to touching the code inStep 2: Subclass StatefulWidget. If you want to try different ways of managing state, skip toManaging state.

Stateful and stateless widgets

#

A widget is either stateful or stateless. If a widget can change—when a user interacts with it, for example—it's stateful.

Astateless widget never changes.Icon,IconButton, andText are examples of stateless widgets. Stateless widgets subclassStatelessWidget.

Astateful widget is dynamic: for example, it can change its appearance in response to events triggered by user interactions or when it receives data.Checkbox,Radio,Slider,InkWell,Form, andTextField are examples of stateful widgets. Stateful widgets subclassStatefulWidget.

A widget's state is stored in aState object, separating the widget's state from its appearance. The state consists of values that can change, like a slider's current value or whether a checkbox is checked. When the widget's state changes, the state object callssetState(), telling the framework to redraw the widget.

Creating a stateful widget

#
What's the point?
  • A stateful widget is implemented by two classes: a subclass ofStatefulWidget and a subclass ofState.
  • The state class contains the widget's mutable state and the widget'sbuild() method.
  • When the widget's state changes, the state object callssetState(), telling the framework to redraw the widget.

In this section, you'll create a custom stateful widget. You'll replace two stateless widgets—the solid red star and the numeric count next to it—with a single custom stateful widget that manages a row with two children widgets: anIconButton andText.

Implementing a custom stateful widget requires creating two classes:

  • A subclass ofStatefulWidget that defines the widget.
  • A subclass ofState that contains the state for that widget and defines the widget'sbuild() method.

This section shows you how to build a stateful widget, calledFavoriteWidget, for the lakes app. After setting up, your first step is choosing how state is managed forFavoriteWidget.

If you've already built the app in thebuilding layouts tutorial, skip to the next section.

  1. Make sure you'veset up your environment.
  2. Create a new Flutter app.
  3. Replace thelib/main.dart file withmain.dart.
  4. Replace thepubspec.yaml file withpubspec.yaml.
  5. Create animages directory in your project, and addlake.jpg.

Once you have a connected and enabled device, or you've launched theiOS simulator (part of the Flutter install) or theAndroid emulator (part of the Android Studio install), you are good to go!

Step 1: Decide which object manages the widget's state

#

A widget's state can be managed in several ways, but in our example the widget itself,FavoriteWidget, will manage its own state. In this example, toggling the star is an isolated action that doesn't affect the parent widget or the rest of the UI, so the widget can handle its state internally.

Learn more about the separation of widget and state, and how state might be managed, inManaging state.

Step 2: Subclass StatefulWidget

#

TheFavoriteWidget class manages its own state, so it overridescreateState() to create aState object. The framework callscreateState() when it wants to build the widget. In this example,createState() returns an instance of_FavoriteWidgetState, which you'll implement in the next step.

dart
classFavoriteWidgetextendsStatefulWidget{constFavoriteWidget({super.key});@overrideState<FavoriteWidget>createState()=>_FavoriteWidgetState();}
Note

Step 3: Subclass State

#

The_FavoriteWidgetState class stores the mutable data that can change over the lifetime of the widget. When the app first launches, the UI displays a solid red star, indicating that the lake has "favorite" status, along with 41 likes. These values are stored in the_isFavorited and_favoriteCount fields:

dart
class_FavoriteWidgetStateextendsState<FavoriteWidget>{bool_isFavorited=true;int_favoriteCount=41;

The class also defines abuild() method, which creates a row containing a redIconButton, andText. You useIconButton (instead ofIcon) because it has anonPressed property that defines the callback function (_toggleFavorite) for handling a tap. You'll define the callback function next.

dart
class_FavoriteWidgetStateextendsState<FavoriteWidget>{// ···@overrideWidgetbuild(BuildContextcontext){returnRow(mainAxisSize:MainAxisSize.min,children:[Container(padding:constEdgeInsets.all(0),child:IconButton(padding:constEdgeInsets.all(0),alignment:Alignment.center,icon:(_isFavorited?constIcon(Icons.star):constIcon(Icons.star_border)),color:Colors.red[500],onPressed:_toggleFavorite,),),SizedBox(width:18,child:SizedBox(child:Text('$_favoriteCount'))),],);}// ···}
Tip

Placing theText in aSizedBox and setting its width prevents a discernible "jump" when the text changes between the values of 40 and 41 — a jump would otherwise occur because those values have different widths.

The_toggleFavorite() method, which is called when theIconButton is pressed, callssetState(). CallingsetState() is critical, because this tells the framework that the widget's state has changed and that the widget should be redrawn. The function argument tosetState() toggles the UI between these two states:

  • Astar icon and the number 41
  • Astar_border icon and the number 40
dart
void_toggleFavorite(){setState((){if(_isFavorited){_favoriteCount-=1;_isFavorited=false;}else{_favoriteCount+=1;_isFavorited=true;}});}

Step 4: Plug the stateful widget into the widget tree

#

Add your custom stateful widget to the widget tree in the app'sbuild() method. First, locate the code that creates theIcon andText, and delete it. In the same location, create the stateful widget:

dart
child:Row(children:[// ...Icon(Icons.star,color:Colors.red[500],),constText('41'),constFavoriteWidget(),],),

That's it! When you hot reload the app, the star icon should now respond to taps.

Problems?

#

If you can't get your code to run, look in your IDE for possible errors.Debugging Flutter apps might help. If you still can't find the problem, check your code against the interactive lakes example on GitHub.

If you still have questions, refer to any one of the developercommunity channels.


The rest of this page covers several ways a widget's state can be managed, and lists other available interactive widgets.

Managing state

#
What's the point?
  • There are different approaches for managing state.
  • You, as the widget designer, choose which approach to use.
  • If in doubt, start by managing state in the parent widget.

Who manages the stateful widget's state? The widget itself? The parent widget? Both? Another object? The answer is... it depends. There are several valid ways to make your widget interactive. You, as the widget designer, make the decision based on how you expect your widget to be used. Here are the most common ways to manage state:

How do you decide which approach to use? The following principles should help you decide:

  • If the state in question is user data, for example the checked or unchecked mode of a checkbox, or the position of a slider, then the state is best managed by the parent widget.

  • If the state in question is aesthetic, for example an animation, then the state is best managed by the widget itself.

If in doubt, start by managing state in the parent widget.

We'll give examples of the different ways of managing state by creating three simple examples: TapboxA, TapboxB, and TapboxC. The examples all work similarly—each creates a container that, when tapped, toggles between a green or grey box. The_active boolean determines the color: green for active or grey for inactive.

Active stateInactive state

These examples useGestureDetector to capture activity on theContainer.

The widget manages its own state

#

Sometimes it makes the most sense for the widget to manage its state internally. For example,ListView automatically scrolls when its content exceeds the render box. Most developers usingListView don't want to manageListView's scrolling behavior, soListView itself manages its scroll offset.

The_TapboxAState class:

  • Manages state forTapboxA.
  • Defines the_active boolean which determines the box's current color.
  • Defines the_handleTap() function, which updates_active when the box is tapped and calls thesetState() function to update the UI.
  • Implements all interactive behavior for the widget.
dart
import'package:flutter/material.dart';// TapboxA manages its own state.//------------------------- TapboxA ----------------------------------classTapboxAextendsStatefulWidget{constTapboxA({super.key});@overrideState<TapboxA>createState()=>_TapboxAState();}class_TapboxAStateextendsState<TapboxA>{bool_active=false;void_handleTap(){setState((){_active=!_active;});}@overrideWidgetbuild(BuildContextcontext){returnGestureDetector(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:constTextStyle(fontSize:32,color:Colors.white),),),),);}}//------------------------- MyApp ----------------------------------classMyAppextendsStatelessWidget{constMyApp({super.key});@overrideWidgetbuild(BuildContextcontext){returnMaterialApp(title:'Flutter Demo',home:Scaffold(appBar:AppBar(title:constText('Flutter Demo')),body:constCenter(child:TapboxA()),),);}}

The parent widget manages the widget's state

#

Often it makes the most sense for the parent widget to manage the state and tell its child widget when to update. For example,IconButton allows you to treat an icon as a tappable button.IconButton is a stateless widget because we decided that the parent widget needs to know whether the button has been tapped, so it can take appropriate action.

In the following example, TapboxB exports its state to its parent through a callback. Because TapboxB doesn't manage any state, it subclasses StatelessWidget.

The ParentWidgetState class:

  • Manages the_active state for TapboxB.
  • Implements_handleTapboxChanged(), the method called when the box is tapped.
  • When the state changes, callssetState() to update the UI.

The TapboxB class:

  • Extends StatelessWidget because all state is handled by its parent.
  • When a tap is detected, it notifies the parent.
dart
import'package:flutter/material.dart';// ParentWidget manages the state for TapboxB.//------------------------ ParentWidget --------------------------------classParentWidgetextendsStatefulWidget{constParentWidget({super.key});@overrideState<ParentWidget>createState()=>_ParentWidgetState();}class_ParentWidgetStateextendsState<ParentWidget>{bool_active=false;void_handleTapboxChanged(boolnewValue){setState((){_active=newValue;});}@overrideWidgetbuild(BuildContextcontext){returnSizedBox(child:TapboxB(active:_active,onChanged:_handleTapboxChanged),);}}//------------------------- TapboxB ----------------------------------classTapboxBextendsStatelessWidget{constTapboxB({super.key,this.active=false,requiredthis.onChanged});finalboolactive;finalValueChanged<bool>onChanged;void_handleTap(){onChanged(!active);}@overrideWidgetbuild(BuildContextcontext){returnGestureDetector(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:constTextStyle(fontSize:32,color:Colors.white),),),),);}}

A mix-and-match approach

#

For some widgets, a mix-and-match approach makes the most sense. In this scenario, the stateful widget manages some of the state, and the parent widget manages other aspects of the state.

In theTapboxC example, on tap down, a dark green border appears around the box. On tap up, the border disappears and the box's color changes.TapboxC exports its_active state to its parent but manages its_highlight state internally. This example has twoState objects,_ParentWidgetState and_TapboxCState.

The_ParentWidgetState object:

  • Manages the_active state.
  • Implements_handleTapboxChanged(), the method called when the box is tapped.
  • CallssetState() to update the UI when a tap occurs and the_active state changes.

The_TapboxCState object:

  • Manages the_highlight state.
  • TheGestureDetector listens to all tap events. As the user taps down, it adds the highlight (implemented as a dark green border). As the user releases the tap, it removes the highlight.
  • CallssetState() to update the UI on tap down, tap up, or tap cancel, and the_highlight state changes.
  • On a tap event, passes that state change to the parent widget to take appropriate action using thewidget property.
dart
import'package:flutter/material.dart';//---------------------------- ParentWidget ----------------------------classParentWidgetextendsStatefulWidget{constParentWidget({super.key});@overrideState<ParentWidget>createState()=>_ParentWidgetState();}class_ParentWidgetStateextendsState<ParentWidget>{bool_active=false;void_handleTapboxChanged(boolnewValue){setState((){_active=newValue;});}@overrideWidgetbuild(BuildContextcontext){returnSizedBox(child:TapboxC(active:_active,onChanged:_handleTapboxChanged),);}}//----------------------------- TapboxC ------------------------------classTapboxCextendsStatefulWidget{constTapboxC({super.key,this.active=false,requiredthis.onChanged});finalboolactive;finalValueChanged<bool>onChanged;@overrideState<TapboxC>createState()=>_TapboxCState();}class_TapboxCStateextendsState<TapboxC>{bool_highlight=false;void_handleTapDown(TapDownDetailsdetails){setState((){_highlight=true;});}void_handleTapUp(TapUpDetailsdetails){setState((){_highlight=false;});}void_handleTapCancel(){setState((){_highlight=false;});}void_handleTap(){widget.onChanged(!widget.active);}@overrideWidgetbuild(BuildContextcontext){// This example adds a green border on tap down.// On tap up, the square changes to the opposite state.returnGestureDetector(onTapDown:_handleTapDown,// Handle the tap events in the order thatonTapUp:_handleTapUp,// they occur: down, up, tap, cancelonTap:_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:constTextStyle(fontSize:32,color:Colors.white),),),),);}}

An alternate implementation might have exported the highlight state to the parent while keeping the active state internal, but if you asked someone to use that tap box, they'd probably complain that it doesn't make much sense. The developer cares whether the box is active. The developer probably doesn't care how the highlighting is managed, and prefers that the tap box handles those details.


Other interactive widgets

#

Flutter offers a variety of buttons and similar interactive widgets. Most of these widgets implement theMaterial Design guidelines, which define a set of components with an opinionated UI.

If you prefer, you can useGestureDetector to build interactivity into any custom widget. You can find examples ofGestureDetector inManaging state. Learn more about theGestureDetector inHandle taps, a recipe in the Flutter cookbook.

Tip

When you need interactivity, it's easiest to use one of the prefabricated widgets. Here's a partial list:

Standard widgets

#

Material Components

#

Resources

#

The following resources might help when adding interactivity to your app.

Gestures, a section in the Flutter cookbook.

Handling gestures

How to create a button and make it respond to input.

Gestures in Flutter

A description of Flutter's gesture mechanism.

Flutter API documentation

Reference documentation for all of the Flutter libraries.

Wonderous apprunning app ,repo

Flutter showcase app with a custom design and engaging interactions.

Flutter's Layered Design (video)

This video includes information about state and stateless widgets. Presented by Google engineer, Ian Hickson.

Was this page's content helpful?

Unless stated otherwise, the documentation on this site reflects Flutter 3.38.6. Page last updated on 2025-11-24.View source orreport an issue.


[8]ページ先頭

©2009-2026 Movatter.jp