<topic_start>Choose your development platform to get startederror Important  If you develop apps in China, check out using Flutter in China.<topic_end><topic_start>Test drive<topic_end><topic_start>What you’ll learn<topic_end><topic_start>Guide depends on your IDEThese tasks depend on which integrated development environment (IDE) you use.Option 1 explains how to code with Visual Studio Code andits Flutter extension.Option 2 explains how to code with Android Studio or IntelliJ IDEA withits Flutter plugin.Flutter supports IntelliJ IDEA Community, Educational, and Ultimate editions.Option 3 explains how to code with an editor of your choice and usethe terminal to compile and debug your code.<topic_end><topic_start>Choose your IDESelect your preferred IDE for Flutter apps.<topic_end><topic_start>Create your sample Flutter appOpen the Command Palette.Go to View > Command Palette orpress  + Shift + P.Type flutterSelect the Flutter: New Project.When prompted for Which Flutter Project, select Application.Create or select the parent directory for the new project folder.When prompted for a Project Name, enter test_drive.Press Enter.Wait for project creation to complete.Open the lib directory, then the main.dart.To learn what each code block does, check out the comments in that Dart file.The previous commands create a Flutter project directory called test_drive thatcontains a simple demo app that uses Material Components.<topic_end><topic_start>Run your sample Flutter appRun your example application on your desktop platform, in the Chrome web browser, in an iOS simulator, orAndroid emulator.Though you can deploy your app to the web,  note that the web target doesn’t support  hot reload at this time.Open the Command Palette.Go to View > Command Palette orpress  + Shift + P.Type flutterSelect the Flutter: Select Device.If no devices are running, this command prompts you to enable a device.Select a target device from Select Device prompt.After you select a target, start the app.Go to Run >Start Debugging or press F5.Wait for the app to launch.You can watch the launch progress in the Debug Console view.After the app build completes, your device displays your app.<topic_end><topic_start>Try hot reloadFlutter offers a fast development cycle with Stateful Hot Reload,the ability to reload the code of a live running app withoutrestarting or losing app state.You can change your app source code, run the hot reload command inVS Code, and see the change in your target device.Open lib/main.dart.Change the word pushed to clicked in the following string.It is on line 109 of the main.dart file as of this writing.error ImportantDon’t stop your app. Let your app run.Save your changes: invoke Save All, or click Hot Reload.Your app updates the string as you watch.<topic_end><topic_start>Create your sample Flutter appLaunch the IDE.Click New Flutter Project at the top of theWelcome to Android Studio dialog.Under Generators, click Flutter.Verify the Flutter SDK path value against the Flutter SDK locationon your development machine.Click Next.Enter test_drive into the Project name field.This follows best practices for naming Flutter projects.Set the directory in the Project location field toC:\dev\test_drive on Microsoft Windows or~/development/test_drive on other platforms.If you didn’t create that directory before, Android Studio displaysa warning that the Directory Does Not Exist. Click Create.From Project type dropdown, select Application.Change the Organization to com.example.flutter.testdrive.Android Studio asks for a company domain name.Android uses the company domain name and project name together as itspackage name when you release the app. iOS uses its Bundle ID.Use com.example.flutter.testdrive to prevent future conflicts.Ignore the remaining form fields. You don’t need to change them forthis test drive. Click Create.Wait for Android Studio to create the project.Open the lib directory, then the main.dart.To learn what each code block does, check out the comments in that Dart file.The previous commands create a Flutter project directorycalled test_drive that contains a simple demo app thatuses Material Components.<topic_end><topic_start>Run your sample Flutter appLocate the main Android Studio toolbar at the top of theAndroid Studio edit window.In the target selector, select an Android device for running the app.You created your Android target device in the Install section.To run your app, make one of the following choices:Click the run icon in the toolbar.Go to Run > Run.Press Ctrl + R.After the app build completes, your device displays your app.<topic_end><topic_start>Try hot reloadFlutter offers a fast development cycle with Stateful Hot Reload,the ability to reload the code of a live running app withoutrestarting or losing app state.You can change your app source code, run the hot reload command inAndroid Studio, and see the change in your target device.Open lib/main.dart.Change the word pushed to clicked in the following string.It is on line 109 of the main.dart file as of this writing.error ImportantDon’t stop your app. Let your app run.Save your changes: invoke Save All, or click Hot Reload.Your app updates the string as you watch.<topic_end><topic_start>Create your sample Flutter appTo create a new Flutter app, run the following commands in your shell or Terminal.Run the flutter create command.The command creates a Flutter project directory called test_drivethat contains a simple demo app that uses Material Components.Change to the Flutter project directory.<topic_end><topic_start>Run your sample Flutter appTo verify that you have a running target device,run the following command.You created your target device in the Install section.To run your app, run the following command.After the app build completes, your device displays your app.<topic_end><topic_start>Try hot reloadFlutter offers a fast development cycle with Stateful Hot Reload,the ability to reload the code of a live running app withoutrestarting or losing app state.You can change your app source code, run the hot reload command inVS Code, and see the change in your target device.Open lib/main.dart.Change the word pushed to clicked in the following string.It is on line 109 of the main.dart file as of this writing.error ImportantDon’t stop your app. Let your app run.Save your changes.Type r in the terminal window.Your app updates the string as you watch.<topic_end><topic_start>Write your first Flutter appYou are now ready to start the “First Flutter app” codelab.In about an hour and a half,you will learn the basics of Flutterby creating an appthat works on mobile, desktop, and web.lightbulb Tip  The codelab above walks you through writing your first Flutter  app for all platforms — mobile, desktop and web.  You might prefer to take  another codelab written specifically for the web.If you prefer an instructor-led version of this codelab,check out the following workshop:<topic_end><topic_start>Learn moreLearn more about the Flutter framework from the following pages:<topic_end><topic_start>Flutter basics<topic_end><topic_start>Apply your existing knowledge<topic_end><topic_start>Other resourcesReach out to us at our mailing list. We’d love to hear from you!Happy Fluttering!<topic_end><topic_start>Flutter for Android developersThis document is meant for Android developers looking to apply theirexisting Android knowledge to build mobile apps with Flutter.If you understand the fundamentals of the Android framework then youcan use this document as a jump start to Flutter development.info Note  To integrate Flutter code into your Android app, see  Add Flutter to existing app.Your Android knowledge and skill set are highly valuable when building withFlutter, because Flutter relies on the mobile operating system for numerouscapabilities and configurations. Flutter is a new way to build UIs for mobile,but it has a plugin system to communicate with Android (and iOS) for non-UItasks. If you’re an expert with Android, you don’t have to relearn everythingto use Flutter.This document can be used as a cookbook by jumping around andfinding questions that are most relevant to your needs.<topic_end><topic_start>Views<topic_end><topic_start>What is the equivalent of a View in Flutter?How is react-style, or declarative, programming different than the  traditional imperative style?  For a comparison, see Introduction to declarative UI.In Android, the View is the foundation of everything that shows up on thescreen. Buttons, toolbars, and inputs, everything is a View.In Flutter, the rough equivalent to a View is a Widget.Widgets don’t map exactly to Android views, but while you’re gettingacquainted with how Flutter works you can think of them as“the way you declare and construct UI”.However, these have a few differences to a View. To start, widgets have adifferent lifespan: they are immutable and only exist until they need to bechanged. Whenever widgets or their state change, Flutter’s framework createsa new tree of widget instances. In comparison, an Android view is drawn onceand does not redraw until invalidate is called.Flutter’s widgets are lightweight, in part due to their immutability.Because they aren’t views themselves, and aren’t directly drawing anything,but rather are a description of the UI and its semantics that get “inflated”into actual view objects under the hood.Flutter includes the Material Components library.These are widgets that implement theMaterial Design guidelines. Material Design is aflexible design system optimized for all platforms,including iOS.But Flutter is flexible and expressive enough to implement any design language.For example, on iOS, you can use the Cupertino widgetsto produce an interface that looks like Apple’s iOS design language.<topic_end><topic_start>How do I update widgets?In Android, you update your views by directly mutating them. However,in Flutter, Widgets are immutable and are not updated directly,instead you have to work with the widget’s state.This is where the concept of Stateful and Stateless widgets comes from.A StatelessWidget is just what it sounds like—awidget with no state information.StatelessWidgets are useful when the part of the user interfaceyou are describing does not depend on anything other than the configurationinformation in the object.For example, in Android, this is similar to placing an ImageViewwith your logo. The logo is not going to change during runtime,so use a StatelessWidget in Flutter.If you want to dynamically change the UI based on data receivedafter making an HTTP call or user interaction then you have to workwith StatefulWidget and tell the Flutter framework that the widget’sState has been updated so it can update that widget.The important thing to note here is at the core both stateless and statefulwidgets behave the same. They rebuild every frame, the difference is theStatefulWidget has a State object that stores state data across framesand restores it.If you are in doubt, then always remember this rule: if a widget changes(because of user interactions, for example) it’s stateful.However, if a widget reacts to change, the containing parent widget canstill be stateless if it doesn’t itself react to change.The following example shows how to use a StatelessWidget. A commonStatelessWidget is the Text widget. If you look at the implementation ofthe Text widget you’ll find that it subclasses StatelessWidget.<code_start>Text(  'I like Flutter!',  style: TextStyle(fontWeight: FontWeight.bold),);<code_end>As you can see, the Text Widget has no state information associated with it,it renders what is passed in its constructors and nothing more.But, what if you want to make “I Like Flutter” change dynamically, forexample when clicking a FloatingActionButton?To achieve this, wrap the Text widget in a StatefulWidget andupdate it when the user clicks the button.For example:<code_start>import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Sample App',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: const SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  // Default placeholder text.  String textToShow = 'I Like Flutter';  void _updateText() {    setState(() {      // Update the text.      textToShow = 'Flutter is Awesome!';    });  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: Center(child: Text(textToShow)),      floatingActionButton: FloatingActionButton(        onPressed: _updateText,        tooltip: 'Update Text',        child: const Icon(Icons.update),      ),    );  }}<code_end><topic_end><topic_start>How do I lay out my widgets? Where is my XML layout file?In Android, you write layouts in XML, but in Flutter you write your layoutswith a widget tree.The following example shows how to display a simple widget with padding:<code_start>@overrideWidget build(BuildContext context) {  return Scaffold(    appBar: AppBar(      title: const Text('Sample App'),    ),    body: Center(      child: ElevatedButton(        style: ElevatedButton.styleFrom(          padding: const EdgeInsets.only(left: 20, right: 30),        ),        onPressed: () {},        child: const Text('Hello'),      ),    ),  );}<code_end>You can view some of the layouts that Flutter has to offer in thewidget catalog.<topic_end><topic_start>How do I add or remove a component from my layout?In Android, you call addChild() or removeChild()on a parent to dynamically add or remove child views.In Flutter, because widgets are immutable there isno direct equivalent to addChild().  Instead,you can pass a function to the parent that returns a widget,and control that child’s creation with a boolean flag.For example, here is how you can toggle between twowidgets when you click on a FloatingActionButton:<code_start>import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Sample App',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: const SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  // Default value for toggle.  bool toggle = true;  void _toggle() {    setState(() {      toggle = !toggle;    });  }  Widget _getToggleChild() {    if (toggle) {      return const Text('Toggle One');    } else {      return ElevatedButton(        onPressed: () {},        child: const Text('Toggle Two'),      );    }  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: Center(        child: _getToggleChild(),      ),      floatingActionButton: FloatingActionButton(        onPressed: _toggle,        tooltip: 'Update Text',        child: const Icon(Icons.update),      ),    );  }}<code_end><topic_end><topic_start>How do I animate a widget?In Android, you either create animations using XML, or call the animate()method on a view. In Flutter, animate widgets using the animationlibrary by wrapping widgets inside an animated widget.In Flutter, use an AnimationController which is an Animation<double>that can pause, seek, stop and reverse the animation. It requires a Tickerthat signals when vsync happens, and produces a linear interpolation between0 and 1 on each frame while it’s running. You then create one or moreAnimations and attach them to the controller.For example, you might use CurvedAnimation to implement an animationalong an interpolated curve. In this sense, the controlleris the “master” source of the animation progress and the CurvedAnimationcomputes the curve that replaces the controller’s default linear motion.Like widgets, animations in Flutter work with composition.When building the widget tree you assign the Animation to an animatedproperty of a widget, such as the opacity of a FadeTransition, and tell thecontroller to start the animation.The following example shows how to write a FadeTransition that fades thewidget into a logo when you press the FloatingActionButton:<code_start>import 'package:flutter/material.dart';void main() {  runApp(const FadeAppTest());}class FadeAppTest extends StatelessWidget {  const FadeAppTest({super.key});  // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Fade Demo',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: const MyFadeTest(title: 'Fade Demo'),    );  }}class MyFadeTest extends StatefulWidget {  const MyFadeTest({super.key, required this.title});  final String title;  @override  State<MyFadeTest> createState() => _MyFadeTest();}class _MyFadeTest extends State<MyFadeTest> with TickerProviderStateMixin {  late AnimationController controller;  late CurvedAnimation curve;  @override  void initState() {    super.initState();    controller = AnimationController(      duration: const Duration(milliseconds: 2000),      vsync: this,    );    curve = CurvedAnimation(      parent: controller,      curve: Curves.easeIn,    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text(widget.title),      ),      body: Center(        child: FadeTransition(          opacity: curve,          child: const FlutterLogo(            size: 100,          ),        ),      ),      floatingActionButton: FloatingActionButton(        tooltip: 'Fade',        onPressed: () {          controller.forward();        },        child: const Icon(Icons.brush),      ),    );  }}<code_end>For more information, seeAnimation & Motion widgets,the Animations tutorial,and the Animations overview.<topic_end><topic_start>How do I use a Canvas to draw/paint?In Android, you would use the Canvas and Drawableto draw images and shapes to the screen.Flutter has a similar Canvas API as well,since it’s based on the same low-level rendering engine, Skia.As a result, painting to a canvas in Flutteris a very familiar task for Android developers.Flutter has two classes that help you draw to the canvas: CustomPaintand CustomPainter,the latter of which implements your algorithm to draw to the canvas.To learn how to implement a signature painter in Flutter,see Collin’s answer on Custom Paint.<code_start>import 'package:flutter/material.dart';void main() => runApp(const MaterialApp(home: DemoApp()));class DemoApp extends StatelessWidget {  const DemoApp({super.key});  @override  Widget build(BuildContext context) => const Scaffold(body: Signature());}class Signature extends StatefulWidget {  const Signature({super.key});  @override  SignatureState createState() => SignatureState();}class SignatureState extends State<Signature> {  List<Offset?> _points = <Offset>[];  @override  Widget build(BuildContext context) {    return GestureDetector(      onPanUpdate: (details) {        setState(() {          RenderBox? referenceBox = context.findRenderObject() as RenderBox;          Offset localPosition =              referenceBox.globalToLocal(details.globalPosition);          _points = List.from(_points)..add(localPosition);        });      },      onPanEnd: (details) => _points.add(null),      child: CustomPaint(        painter: SignaturePainter(_points),        size: Size.infinite,      ),    );  }}class SignaturePainter extends CustomPainter {  SignaturePainter(this.points);  final List<Offset?> points;  @override  void paint(Canvas canvas, Size size) {    var paint = Paint()      ..color = Colors.black      ..strokeCap = StrokeCap.round      ..strokeWidth = 5;    for (int i = 0; i < points.length - 1; i++) {      if (points[i] != null && points[i + 1] != null) {        canvas.drawLine(points[i]!, points[i + 1]!, paint);      }    }  }  @override  bool shouldRepaint(SignaturePainter oldDelegate) =>      oldDelegate.points != points;}<code_end><topic_end><topic_start>How do I build custom widgets?In Android, you typically subclass View, or use a pre-existing view,to override and implement methods that achieve the desired behavior.In Flutter, build a custom widget by composingsmaller widgets (instead of extending them).It is somewhat similar to implementing a custom ViewGroupin Android, where all the building blocks are already existing,but you provide a different behavior—for example,custom layout logic.For example, how do you build a CustomButton that takes a label inthe constructor? Create a CustomButton that composes a ElevatedButton witha label, rather than by extending ElevatedButton:<code_start>class CustomButton extends StatelessWidget {  final String label;  const CustomButton(this.label, {super.key});  @override  Widget build(BuildContext context) {    return ElevatedButton(      onPressed: () {},      child: Text(label),    );  }}<code_end>Then use CustomButton, just as you’d use any other Flutter widget:<code_start>@overrideWidget build(BuildContext context) {  return const Center(    child: CustomButton('Hello'),  );}<code_end><topic_end><topic_start>Intents<topic_end><topic_start>What is the equivalent of an Intent in Flutter?In Android, there are two main use cases for Intents: navigating betweenActivities, and communicating with components. Flutter, on the other hand,does not have the concept of intents, although you can still start intentsthrough native integrations (using a plugin).Flutter doesn’t really have a direct equivalent to activities and fragments;rather, in Flutter you navigate between screens, using a Navigator andRoutes, all within the same Activity.A Route is an abstraction for a “screen” or “page” of an app, and aNavigator is a widget that manages routes. A route roughly maps to anActivity, but it does not carry the same meaning. A navigator can pushand pop routes to move from screen to screen. Navigators work like a stackon which you can push() new routes you want to navigate to, and fromwhich you can pop() routes when you want to “go back”.In Android, you declare your activities inside the app’s AndroidManifest.xml.In Flutter, you have a couple options to navigate between pages:The following example builds a Map.<code_start>void main() {  runApp(MaterialApp(    home: const MyAppHome(), // Becomes the route named '/'.    routes: <String, WidgetBuilder>{      '/a': (context) => const MyPage(title: 'page A'),      '/b': (context) => const MyPage(title: 'page B'),      '/c': (context) => const MyPage(title: 'page C'),    },  ));}<code_end>Navigate to a route by pushing its name to the Navigator.<code_start>Navigator.of(context).pushNamed('/b');<code_end>The other popular use-case for Intents is to call external components suchas a Camera or File picker. For this, you would need to create a native platformintegration (or use an existing plugin).To learn how to build a native platform integration,see developing packages and plugins.<topic_end><topic_start>How do I handle incoming intents from external applications in Flutter?Flutter can handle incoming intents from Android by directly talking to theAndroid layer and requesting the data that was shared.The following example registers a text share intent filter on the nativeactivity that runs our Flutter code, so other apps can share text withour Flutter app.The basic flow implies that we first handle the shared text data on theAndroid native side (in our Activity), and then wait until Flutter requestsfor the data to provide it using a MethodChannel.First, register the intent filter for all intents in AndroidManifest.xml:Then in MainActivity, handle the intent, extract the text that wasshared from the intent, and hold onto it. When Flutter is ready to process,it requests the data using a platform channel, and it’s sentacross from the native side:Finally, request the data from the Flutter sidewhen the widget is rendered:<code_start>import 'package:flutter/material.dart';import 'package:flutter/services.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Sample Shared App Handler',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: const SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  static const platform = MethodChannel('app.channel.shared.data');  String dataShared = 'No data';  @override  void initState() {    super.initState();    getSharedText();  }  @override  Widget build(BuildContext context) {    return Scaffold(body: Center(child: Text(dataShared)));  }  Future<void> getSharedText() async {    var sharedData = await platform.invokeMethod('getSharedText');    if (sharedData != null) {      setState(() {        dataShared = sharedData;      });    }  }}<code_end><topic_end><topic_start>What is the equivalent of startActivityForResult()?The Navigator class handles routing in Flutter and is used to geta result back from a route that you have pushed on the stack.This is done by awaiting on the Future returned by push().For example, to start a location route that lets the user selecttheir location, you could do the following:<code_start>Object? coordinates = await Navigator.of(context).pushNamed('/location');<code_end>And then, inside your location route, once the user has selected their locationyou can pop the stack with the result:<code_start>Navigator.of(context).pop({'lat': 43.821757, 'long': -79.226392});<code_end><topic_end><topic_start>Async UI<topic_end><topic_start>What is the equivalent of runOnUiThread() in Flutter?Dart has a single-threaded execution model, with support for Isolates(a way to run Dart code on another thread), an event loop, andasynchronous programming. Unless you spawn an Isolate, your Dart coderuns in the main UI thread and is driven by an event loop. Flutter’s eventloop is equivalent to Android’s main Looper—that is, the Looper thatis attached to the main thread.Dart’s single-threaded model doesn’t mean you need to run everything as ablocking operation that causes the UI to freeze. Unlike Android, whichrequires you to keep the main thread free at all times, in Flutter,use the asynchronous facilities that the Dart language provides, such asasync/await, to perform asynchronous work. You might be familiar withthe async/await paradigm if you’ve used it in C#, Javascript, or if youhave used Kotlin’s coroutines.For example, you can run network code without causing the UI to hang byusing async/await and letting Dart do the heavy lifting:<code_start>Future<void> loadData() async {  var dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');  http.Response response = await http.get(dataURL);  setState(() {    widgets = jsonDecode(response.body);  });}<code_end>Once the awaited network call is done, update the UI by calling setState(),which triggers a rebuild of the widget subtree and updates the data.The following example loads data asynchronously and displays it in a ListView:<code_start>import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Sample App',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: const SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List widgets = [];  @override  void initState() {    super.initState();    loadData();  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: ListView.builder(        itemCount: widgets.length,        itemBuilder: (context, position) {          return getRow(position);        },      ),    );  }  Widget getRow(int i) {    return Padding(      padding: const EdgeInsets.all(10),      child: Text("Row ${widgets[i]["title"]}"),    );  }  Future<void> loadData() async {    var dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');    http.Response response = await http.get(dataURL);    setState(() {      widgets = jsonDecode(response.body);    });  }}<code_end>Refer to the next section for more information on doing work in thebackground, and how Flutter differs from Android.<topic_end><topic_start>How do you move work to a background thread?In Android, when you want to access a network resource you would typicallymove to a background thread and do the work, as to not block the main thread,and avoid ANRs. For example, you might be using an AsyncTask, a LiveData,an IntentService, a JobScheduler job, or an RxJava pipeline with ascheduler that works on background threads.Since Flutter is single threaded and runs an event loop (like Node.js), youdon’t have to worry about thread management or spawning background threads. Ifyou’re doing I/O-bound work, such as disk access or a network call, thenyou can safely use async/await and you’re all set. If, on the otherhand, you need to do computationally intensive work that keeps the CPU busy,you want to move it to an Isolate to avoid blocking the event loop, likeyou would keep any sort of work out of the main thread in Android.For I/O-bound work, declare the function as an async function,and await on long-running tasks inside the function:<code_start>Future<void> loadData() async {  var dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');  http.Response response = await http.get(dataURL);  setState(() {    widgets = jsonDecode(response.body);  });}<code_end>This is how you would typically do network or database calls, which are bothI/O operations.On Android, when you extend AsyncTask, you typically override 3 methods,onPreExecute(), doInBackground() and onPostExecute(). There is noequivalent in Flutter, since you await on a long-running function, andDart’s event loop takes care of the rest.However, there are times when you might be processing a large amount of data andyour UI hangs. In Flutter, use Isolates to take advantage ofmultiple CPU cores to do long-running or computationally intensive tasks.Isolates are separate execution threads that do not share any memorywith the main execution memory heap. This means you can’t access variables fromthe main thread, or update your UI by calling setState().Unlike Android threads,Isolates are true to their name, and cannot share memory(in the form of static fields, for example).The following example shows, in a simple isolate, how to share data back tothe main thread to update the UI.<code_start>Future<void> loadData() async {  ReceivePort receivePort = ReceivePort();  await Isolate.spawn(dataLoader, receivePort.sendPort);  // The 'echo' isolate sends its SendPort as the first message.  SendPort sendPort = await receivePort.first;  List msg = await sendReceive(    sendPort,    'https://jsonplaceholder.typicode.com/posts',  );  setState(() {    widgets = msg;  });}// The entry point for the isolate.static Future<void> dataLoader(SendPort sendPort) async {  // Open the ReceivePort for incoming messages.  ReceivePort port = ReceivePort();  // Notify any other isolates what port this isolate listens to.  sendPort.send(port.sendPort);  await for (var msg in port) {    String data = msg[0];    SendPort replyTo = msg[1];    String dataURL = data;    http.Response response = await http.get(Uri.parse(dataURL));    // Lots of JSON to parse    replyTo.send(jsonDecode(response.body));  }}Future sendReceive(SendPort port, msg) {  ReceivePort response = ReceivePort();  port.send([msg, response.sendPort]);  return response.first;}<code_end>Here, dataLoader() is the Isolate that runs in its own separateexecution thread.  In the isolate you can perform more CPU intensiveprocessing (parsing a big JSON, for example),or perform computationally intensive math,such as encryption or signal processing.You can run the full example below:<code_start>import 'dart:async';import 'dart:convert';import 'dart:isolate';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Sample App',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: const SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List widgets = [];  @override  void initState() {    super.initState();    loadData();  }  Widget getBody() {    bool showLoadingDialog = widgets.isEmpty;    if (showLoadingDialog) {      return getProgressDialog();    } else {      return getListView();    }  }  Widget getProgressDialog() {    return const Center(child: CircularProgressIndicator());  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: getBody(),    );  }  ListView getListView() {    return ListView.builder(      itemCount: widgets.length,      itemBuilder: (context, position) {        return getRow(position);      },    );  }  Widget getRow(int i) {    return Padding(      padding: const EdgeInsets.all(10),      child: Text("Row ${widgets[i]["title"]}"),    );  }  Future<void> loadData() async {    ReceivePort receivePort = ReceivePort();    await Isolate.spawn(dataLoader, receivePort.sendPort);    // The 'echo' isolate sends its SendPort as the first message.    SendPort sendPort = await receivePort.first;    List msg = await sendReceive(      sendPort,      'https://jsonplaceholder.typicode.com/posts',    );    setState(() {      widgets = msg;    });  }  // The entry point for the isolate.  static Future<void> dataLoader(SendPort sendPort) async {    // Open the ReceivePort for incoming messages.    ReceivePort port = ReceivePort();    // Notify any other isolates what port this isolate listens to.    sendPort.send(port.sendPort);    await for (var msg in port) {      String data = msg[0];      SendPort replyTo = msg[1];      String dataURL = data;      http.Response response = await http.get(Uri.parse(dataURL));      // Lots of JSON to parse      replyTo.send(jsonDecode(response.body));    }  }  Future sendReceive(SendPort port, msg) {    ReceivePort response = ReceivePort();    port.send([msg, response.sendPort]);    return response.first;  }}<code_end><topic_end><topic_start>What is the equivalent of OkHttp on Flutter?Making a network call in Flutter is easy when you use thepopular http package.While the http package doesn’t have every feature found in OkHttp,it abstracts away much of the networking that you would normally implementyourself, making it a simple way to make network calls.To add the http package as a dependency, run flutter pub add:To make a network call, call await on the async function http.get():<code_start>import 'dart:developer' as developer;import 'package:http/http.dart' as http;Future<void> loadData() async {  var dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');  http.Response response = await http.get(dataURL);  developer.log(response.body);}<code_end><topic_end><topic_start>How do I show the progress for a long-running task?In Android you would typically show a ProgressBar view in your UI whileexecuting a long-running task on a background thread.In Flutter, use a ProgressIndicator widget.Show the progress programmatically by controlling when it’s renderedthrough a boolean flag. Tell Flutter to update its state before yourlong-running task starts, and hide it after it ends.In the following example, the build function is separated into three differentfunctions. If showLoadingDialog is true (when widgets.isEmpty),then render the ProgressIndicator. Otherwise, render theListView with the data returned from a network call.<code_start>import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Sample App',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: const SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List widgets = [];  @override  void initState() {    super.initState();    loadData();  }  Widget getBody() {    bool showLoadingDialog = widgets.isEmpty;    if (showLoadingDialog) {      return getProgressDialog();    } else {      return getListView();    }  }  Widget getProgressDialog() {    return const Center(child: CircularProgressIndicator());  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: getBody(),    );  }  ListView getListView() {    return ListView.builder(      itemCount: widgets.length,      itemBuilder: (context, position) {        return getRow(position);      },    );  }  Widget getRow(int i) {    return Padding(      padding: const EdgeInsets.all(10),      child: Text("Row ${widgets[i]["title"]}"),    );  }  Future<void> loadData() async {    var dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');    http.Response response = await http.get(dataURL);    setState(() {      widgets = jsonDecode(response.body);    });  }}<code_end><topic_end><topic_start>Project structure & resources<topic_end><topic_start>Where do I store my resolution-dependent image files?While Android treats resources and assets as distinct items,Flutter apps have only assets. All resources that would livein the res/drawable-* folders on Android,are placed in an assets folder for Flutter.Flutter follows a simple density-based format like iOS.Assets might be 1.0x, 2.0x, 3.0x, or any other multiplier.Flutter doesn’t have dps but there are logical pixels,which are basically the same as device-independent pixels.Flutter’s devicePixelRatio expresses the ratioof physical pixels in a single logical pixel.The equivalent to Android’s density buckets are:Assets are located in any arbitrary folder—Flutterhas no predefined folder structure.You declare the assets (with location) inthe pubspec.yaml file, and Flutter picks them up.Assets stored in the native asset folder areaccessed on the native side using Android’s AssetManager:Flutter can’t access native resources or assets.To add a new image asset called my_icon.png to our Flutter project,for example, and deciding that it should live in a folder wearbitrarily called images, you would put the base image (1.0x)in the images folder, and all the other variants in sub-folderscalled with the appropriate ratio multiplier:Next, you’ll need to declare these images in your pubspec.yaml file:You can then access your images using AssetImage:<code_start>AssetImage('images/my_icon.jpeg')<code_end>or directly in an Image widget:<code_start>@overrideWidget build(BuildContext context) {  return Image.asset('images/my_image.png');}<code_end><topic_end><topic_start>Where do I store strings? How do I handle localization?Flutter currently doesn’t have a dedicated resources-like system for strings.The best and recommended practice is to hold your strings in a .arb file as key-value pairs For example:<code_start>{   "@@locale": "en",   "hello":"Hello {userName}",   "@hello":{      "description":"A message with a single parameter",      "placeholders":{         "userName":{            "type":"String",            "example":"Bob"         }      }   }}<code_end>Then in your code, you can access your strings as such:<code_start>Text(AppLocalizations.of(context)!.hello('John'));<code_end>Flutter has basic support for accessibility on Android,though this feature is a work in progress.See Internationalizing Flutter apps for more information on this.<topic_end><topic_start>What is the equivalent of a Gradle file? How do I add dependencies?In Android, you add dependencies by adding to your Gradle build script.Flutter uses Dart’s own build system, and the Pub package manager.The tools delegate the building of the native Android and iOSwrapper apps to the respective build systems.While there are Gradle files under the android folder in yourFlutter project, only use these if you are adding nativedependencies needed for per-platform integration.In general, use pubspec.yaml to declareexternal dependencies to use in Flutter.A good place to find Flutter packages is pub.dev.<topic_end><topic_start>Activities and fragments<topic_end><topic_start>What are the equivalent of activities and fragments in Flutter?In Android, an Activity represents a single focused thing the user can do.A Fragment represents a behavior or a portion of user interface.Fragments are a way to modularize your code, compose sophisticateduser interfaces for larger screens, and help scale your application UI.In Flutter, both of these concepts fall under the umbrella of Widgets.To learn more about the UI for building Activities and Fragments,see the community-contributed Medium article,Flutter for Android Developers: How to design Activity UI in Flutter.As mentioned in the Intents section,screens in Flutter are represented by Widgets since everything isa widget in Flutter. Use a Navigator to move between differentRoutes that represent different screens or pages,or perhaps different states or renderings of the same data.<topic_end><topic_start>How do I listen to Android activity lifecycle events?In Android, you can override methods from the Activity to capture lifecyclemethods for the activity itself, or register ActivityLifecycleCallbacks onthe Application. In Flutter, you have neither concept, but you can insteadlisten to lifecycle events by hooking into the WidgetsBinding observer andlistening to the didChangeAppLifecycleState() change event.The observable lifecycle events are:For more details on the meaning of these states, see theAppLifecycleStatus documentation.As you might have noticed, only a small minority of the Activitylifecycle events are available; while FlutterActivity doescapture almost all the activity lifecycle events internally andsend them over to the Flutter engine, they’re mostly shieldedaway from you. Flutter takes care of starting and stopping theengine for you, and there is little reason for needing toobserve the activity lifecycle on the Flutter side in most cases.If you need to observe the lifecycle to acquire or release anynative resources, you should likely be doing it from the native side,at any rate.Here’s an example of how to observe the lifecycle status of thecontaining activity:<code_start>import 'package:flutter/widgets.dart';class LifecycleWatcher extends StatefulWidget {  const LifecycleWatcher({super.key});  @override  State<LifecycleWatcher> createState() => _LifecycleWatcherState();}class _LifecycleWatcherState extends State<LifecycleWatcher>    with WidgetsBindingObserver {  AppLifecycleState? _lastLifecycleState;  @override  void initState() {    super.initState();    WidgetsBinding.instance.addObserver(this);  }  @override  void dispose() {    WidgetsBinding.instance.removeObserver(this);    super.dispose();  }  @override  void didChangeAppLifecycleState(AppLifecycleState state) {    setState(() {      _lastLifecycleState = state;    });  }  @override  Widget build(BuildContext context) {    if (_lastLifecycleState == null) {      return const Text(        'This widget has not observed any lifecycle changes.',        textDirection: TextDirection.ltr,      );    }    return Text(      'The most recent lifecycle state this widget observed was: $_lastLifecycleState.',      textDirection: TextDirection.ltr,    );  }}void main() {  runApp(const Center(child: LifecycleWatcher()));}<code_end><topic_end><topic_start>Layouts<topic_end><topic_start>What is the equivalent of a LinearLayout?In Android, a LinearLayout is used to lay your widgets outlinearly—either horizontally or vertically.In Flutter, use the Row or Columnwidgets to achieve the same result.If you notice the two code samples are identical with the exception of the“Row” and “Column” widget. The children are the same and this feature can beexploited to develop rich layouts that can change overtime with the samechildren.<code_start>@overrideWidget build(BuildContext context) {  return const Row(    mainAxisAlignment: MainAxisAlignment.center,    children: <Widget>[      Text('Row One'),      Text('Row Two'),      Text('Row Three'),      Text('Row Four'),    ],  );}<code_end><code_start>@overrideWidget build(BuildContext context) {  return const Column(    mainAxisAlignment: MainAxisAlignment.center,    children: <Widget>[      Text('Column One'),      Text('Column Two'),      Text('Column Three'),      Text('Column Four'),    ],  );}<code_end>To learn more about building linear layouts,see the community-contributed Medium articleFlutter for Android Developers: How to design LinearLayout in Flutter.<topic_end><topic_start>What is the equivalent of a RelativeLayout?A RelativeLayout lays your widgets out relative to each other. InFlutter, there are a few ways to achieve the same result.You can achieve the result of a RelativeLayout by using a combination ofColumn, Row, and Stack widgets. You can specify rules for the widgetsconstructors on how the children are laid out relative to the parent.For a good example of building a RelativeLayout in Flutter,see Collin’s answer on StackOverflow.<topic_end><topic_start>What is the equivalent of a ScrollView?In Android, use a ScrollView to lay out your widgets—if the user’sdevice has a smaller screen than your content, it scrolls.In Flutter, the easiest way to do this is using the ListView widget.This might seem like overkill coming from Android,but in Flutter a ListView widget isboth a ScrollView and an Android ListView.<code_start>@overrideWidget build(BuildContext context) {  return ListView(    children: const <Widget>[      Text('Row One'),      Text('Row Two'),      Text('Row Three'),      Text('Row Four'),    ],  );}<code_end><topic_end><topic_start>How do I handle landscape transitions in Flutter?FlutterView handles the config change if AndroidManifest.xml contains:<topic_end><topic_start>Gesture detection and touch event handling<topic_end><topic_start>How do I add an onClick listener to a widget in Flutter?In Android, you can attach onClick to views such as button by callingthe method ‘setOnClickListener’.In Flutter there are two ways of adding touch listeners:<code_start>@override  Widget build(BuildContext context) {    return ElevatedButton(      onPressed: () {        developer.log('click');      },      child: const Text('Button'),    );  }<code_end><code_start>class SampleTapApp extends StatelessWidget {    const SampleTapApp({super.key});    @override    Widget build(BuildContext context) {      return Scaffold(        body: Center(          child: GestureDetector(            onTap: () {              developer.log('tap');            },            child: const FlutterLogo(              size: 200,            ),          ),        ),      );    }  }<code_end><topic_end><topic_start>How do I handle other gestures on widgets?Using the GestureDetector, you can listen to a wide range of Gestures such as:TapDouble tapLong pressVertical dragHorizontal dragThe following example shows a GestureDetectorthat rotates the Flutter logo on a double tap:<code_start>class SampleApp extends StatefulWidget {  const SampleApp({super.key});  @override  State<SampleApp> createState() => _SampleAppState();}class _SampleAppState extends State<SampleApp>    with SingleTickerProviderStateMixin {  late AnimationController controller;  late CurvedAnimation curve;  @override  void initState() {    super.initState();    controller = AnimationController(      vsync: this,      duration: const Duration(milliseconds: 2000),    );    curve = CurvedAnimation(      parent: controller,      curve: Curves.easeIn,    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      body: Center(        child: GestureDetector(          onDoubleTap: () {            if (controller.isCompleted) {              controller.reverse();            } else {              controller.forward();            }          },          child: RotationTransition(            turns: curve,            child: const FlutterLogo(              size: 200,            ),          ),        ),      ),    );  }}<code_end><topic_end><topic_start>Listviews & adapters<topic_end><topic_start>What is the alternative to a ListView in Flutter?The equivalent to a ListView in Flutter is … a ListView!In an Android ListView, you create an adapter and pass it into theListView, which renders each row with what your adapter returns. However, youhave to make sure you recycle your rows, otherwise, you get all sorts of crazyvisual glitches and memory issues.Due to Flutter’s immutable widget pattern, you pass a list ofwidgets to your ListView, and Flutter takes care of making surethat scrolling is fast and smooth.<code_start>import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Sample App',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: const SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: ListView(children: _getListData()),    );  }  List<Widget> _getListData() {    List<Widget> widgets = [];    for (int i = 0; i < 100; i++) {      widgets.add(Padding(        padding: const EdgeInsets.all(10),        child: Text('Row $i'),      ));    }    return widgets;  }}<code_end><topic_end><topic_start>How do I know which list item is clicked on?In Android, the ListView has a method to find out which item was clicked,‘onItemClickListener’.In Flutter, use the touch handling provided by the passed-in widgets.<code_start>import 'dart:developer' as developer;import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Sample App',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: const SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: ListView(children: _getListData()),    );  }  List<Widget> _getListData() {    List<Widget> widgets = [];    for (int i = 0; i < 100; i++) {      widgets.add(        GestureDetector(          onTap: () {            developer.log('row tapped');          },          child: Padding(            padding: const EdgeInsets.all(10),            child: Text('Row $i'),          ),        ),      );    }    return widgets;  }}<code_end><topic_end><topic_start>How do I update ListView’s dynamically?On Android, you update the adapter and call notifyDataSetChanged.In Flutter, if you were to update the list of widgets inside a setState(),you would quickly see that your data did not change visually.This is because when setState() is called, the Flutter rendering enginelooks at the widget tree to see if anything has changed. When it gets to yourListView, it performs a == check, and determines that the twoListViews are the same. Nothing has changed, so no update is required.For a simple way to update your ListView, create a new List inside ofsetState(), and copy the data from the old list to the new list.While this approach is simple, it is not recommended for large data sets,as shown in the next example.<code_start>import 'dart:developer' as developer;import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Sample App',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: const SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List<Widget> widgets = [];  @override  void initState() {    super.initState();    for (int i = 0; i < 100; i++) {      widgets.add(getRow(i));    }  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: ListView(children: widgets),    );  }  Widget getRow(int i) {    return GestureDetector(      onTap: () {        setState(() {          widgets = List.from(widgets);          widgets.add(getRow(widgets.length));          developer.log('row $i');        });      },      child: Padding(        padding: const EdgeInsets.all(10),        child: Text('Row $i'),      ),    );  }}<code_end>The recommended, efficient, and effective way to build a list uses aListView.Builder. This method is great when you have a dynamicList or a List with very large amounts of data. This is essentiallythe equivalent of RecyclerView on Android, which automaticallyrecycles list elements for you:<code_start>import 'dart:developer' as developer;import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Sample App',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: const SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List<Widget> widgets = [];  @override  void initState() {    super.initState();    for (int i = 0; i < 100; i++) {      widgets.add(getRow(i));    }  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: ListView.builder(        itemCount: widgets.length,        itemBuilder: (context, position) {          return getRow(position);        },      ),    );  }  Widget getRow(int i) {    return GestureDetector(      onTap: () {        setState(() {          widgets.add(getRow(widgets.length));          developer.log('row $i');        });      },      child: Padding(        padding: const EdgeInsets.all(10),        child: Text('Row $i'),      ),    );  }}<code_end>Instead of creating a “ListView”, create aListView.builder that takes two key parameters: theinitial length of the list, and an ItemBuilder function.The ItemBuilder function is similar to the getViewfunction in an Android adapter; it takes a position,and returns the row you want rendered at that position.Finally, but most importantly, notice that the onTap() functiondoesn’t recreate the list anymore, but instead .adds to it.<topic_end><topic_start>Working with text<topic_end><topic_start>How do I set custom fonts on my Text widgets?In Android SDK (as of Android O), you create a Font resource file andpass it into the FontFamily param for your TextView.In Flutter, place the font file in a folder and reference it in thepubspec.yaml file, similar to how you import images.Then assign the font to your Text widget:<code_start>@overrideWidget build(BuildContext context) {  return Scaffold(    appBar: AppBar(      title: const Text('Sample App'),    ),    body: const Center(      child: Text(        'This is a custom font text',        style: TextStyle(fontFamily: 'MyCustomFont'),      ),    ),  );}<code_end><topic_end><topic_start>How do I style my Text widgets?Along with fonts, you can customize other styling elements on a Text widget.The style parameter of a Text widget takes a TextStyle object, where you cancustomize many parameters, such as:<topic_end><topic_start>Form inputFor more information on using Forms,see Retrieve the value of a text field,from the Flutter cookbook.<topic_end><topic_start>What is the equivalent of a “hint” on an Input?In Flutter, you can easily show a “hint” or a placeholder text for your input byadding an InputDecoration object to the decoration constructor parameter forthe Text Widget.<code_start>Center(  child: TextField(    decoration: InputDecoration(hintText: 'This is a hint'),  ),)<code_end><topic_end><topic_start>How do I show validation errors?Just as you would with a “hint”, pass an InputDecoration objectto the decoration constructor for the Text widget.However, you don’t want to start off by showing an error.Instead, when the user has entered invalid data,update the state, and pass a new InputDecoration object.<code_start>import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Sample App',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: const SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  String? _errorText;  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: Center(        child: TextField(          onSubmitted: (text) {            setState(() {              if (!isEmail(text)) {                _errorText = 'Error: This is not an email';              } else {                _errorText = null;              }            });          },          decoration: InputDecoration(            hintText: 'This is a hint',            errorText: _getErrorText(),          ),        ),      ),    );  }  String? _getErrorText() {    return _errorText;  }  bool isEmail(String em) {    String emailRegexp =        r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|'        r'(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|'        r'(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';    RegExp regExp = RegExp(emailRegexp);    return regExp.hasMatch(em);  }}<code_end><topic_end><topic_start>Flutter plugins<topic_end><topic_start>How do I access the GPS sensor?Use the geolocator community plugin.<topic_end><topic_start>How do I access the camera?The image_picker plugin is popularfor accessing the camera.<topic_end><topic_start>How do I log in with Facebook?To Log in with Facebook, use theflutter_facebook_login community plugin.<topic_end><topic_start>How do I use Firebase features?Most Firebase functions are covered byfirst party plugins.These plugins are first-party integrations,maintained by the Flutter team:You can also find some third-party Firebase plugins onpub.dev that cover areas not directly covered by thefirst-party plugins.<topic_end><topic_start>How do I build my own custom native integrations?If there is platform-specific functionality that Flutteror its community Plugins are missing,you can build your own following thedeveloping packages and plugins page.Flutter’s plugin architecture, in a nutshell, is much like using an Event bus inAndroid: you fire off a message and let the receiver process and emit a resultback to you. In this case, the receiver is code running on the native sideon Android or iOS.<topic_end><topic_start>How do I use the NDK in my Flutter application?If you use the NDK in your current Android application and want your Flutterapplication to take advantage of your native libraries then it’s possible bybuilding a custom plugin.Your custom plugin first talks to your Android app, where you call yournative functions over JNI. Once a response is ready,send a message back to Flutter and render the result.Calling native code directly from Flutter is currently not supported.<topic_end><topic_start>Themes<topic_end><topic_start>How do I theme my app?Out of the box, Flutter comes with a beautiful implementation of MaterialDesign, which takes care of a lot of styling and theming needs that you wouldtypically do. Unlike Android where you declare themes in XML and then assign itto your application using AndroidManifest.xml, in Flutter you declare themesin the top level widget.To take full advantage of Material Components in your app, you can declare a toplevel widget MaterialApp as the entry point to your application. MaterialAppis a convenience widget that wraps a number of widgets that are commonlyrequired for applications implementing Material Design.It builds upon a WidgetsApp by adding Material specific functionality.You can also use a WidgetsApp as your app widget, which provides some of thesame functionality, but is not as rich as MaterialApp.To customize the colors and styles of any child components, pass aThemeData object to the MaterialApp widget. For example, in the code below,the color scheme from seed is set to deepPurple and text selection color is red.<code_start>import 'package:flutter/material.dart';class SampleApp extends StatelessWidget {  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Sample App',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),        textSelectionTheme:            const TextSelectionThemeData(selectionColor: Colors.red),      ),      home: const SampleAppPage(),    );  }}<code_end><topic_end><topic_start>Databases and local storage<topic_end><topic_start>How do I access Shared Preferences?In Android, you can store a small collection of key-value pairs usingthe SharedPreferences API.In Flutter, access this functionality using theShared_Preferences plugin.This plugin wraps the functionality of bothShared Preferences and NSUserDefaults (the iOS equivalent).<code_start>import 'dart:async';import 'package:flutter/material.dart';import 'package:shared_preferences/shared_preferences.dart';void main() {  runApp(    const MaterialApp(      home: Scaffold(        body: Center(          child: ElevatedButton(            onPressed: _incrementCounter,            child: Text('Increment Counter'),          ),        ),      ),    ),  );}Future<void> _incrementCounter() async {  SharedPreferences prefs = await SharedPreferences.getInstance();  int counter = (prefs.getInt('counter') ?? 0) + 1;  await prefs.setInt('counter', counter);}<code_end><topic_end><topic_start>How do I access SQLite in Flutter?In Android, you use SQLite to store structured datathat you can query using SQL.In Flutter, for macOS, Android, or iOS,access this functionality using theSQFlite plugin.<topic_end><topic_start>Debugging<topic_end><topic_start>What tools can I use to debug my app in Flutter?Use the DevTools suite for debugging Flutter or Dart apps.DevTools includes support for profiling, examining the heap,inspecting the widget tree, logging diagnostics, debugging,observing executed lines of code, debugging memory leaks and memoryfragmentation. For more information, see theDevTools documentation.<topic_end><topic_start>Notifications<topic_end><topic_start>How do I set up push notifications?In Android, you use Firebase Cloud Messaging to set uppush notifications for your app.In Flutter, access this functionality using theFirebase Messaging plugin.For more information on using the Firebase Cloud Messaging API,see the firebase_messaging plugin documentation.<topic_end><topic_start>Flutter for SwiftUI DevelopersSwiftUI developers who want to write mobile apps using Fluttershould review this guide.It explains how to apply existing SwiftUI knowledge to Flutter.info Note  If you instead have experience building apps for iOS with UIKit,  see Flutter for UIKit developers.Flutter is a framework for building cross-platform applicationsthat uses the Dart programming language.To understand some differences between programming with Dartand programming with Swift, see Learning Dart as a Swift Developerand Flutter concurrency for Swift developers.Your SwiftUI knowledge and experienceare highly valuable when building with Flutter.Flutter also makes a number of adaptationsto app behavior when running on iOS and macOS.To learn how, see Platform adaptations.info  To integrate Flutter code into an existing iOS app,  check out Add Flutter to existing app.This document can be used as a cookbook by jumping aroundand finding questions that are most relevant to your needs.This guide embeds sample code.You can test full working examples on DartPad or view them on GitHub.<topic_end><topic_start>OverviewAs an introduction, watch the following video.It outlines how Flutter works on iOS and how to use Flutter to build iOS apps.Flutter and SwiftUI code describes how the UI looks and works.Developers call this type of code a declarative framework.<topic_end><topic_start>Views vs. WidgetsSwiftUI represents UI components as views.You configure views using modifiers.Flutter represents UI components as widgets.Both views and widgets only exist until they need to be changed.These languages call this property immutability.SwiftUI represents a UI component property as a View modifier.By contrast, Flutter uses widgets for both UI components andtheir properties.To compose layouts, both SwiftUI and Flutter nest UI componentswithin one another.SwiftUI nests Views while Flutter nests Widgets.<topic_end><topic_start>Layout processSwiftUI lays out views using the following process:Flutter differs somewhat with its process:Flutter differs from SwiftUI because the parent component can overridethe child’s desired size. The widget cannot have any size it wants.It also cannot know or decide its position on screen as its parentmakes that decision.To force a child widget to render at a specific size,the parent must set tight constraints.A constraint becomes tight when its constraint’s minimum size valueequals its maximum size value.In SwiftUI, views might expand to the available space orlimit their size to that of its content.Flutter widgets behave in similar manner.However, in Flutter parent widgets can offer unbounded constraints.Unbounded constraints set their maximum values to infinity.If the child expands and it has unbounded constraints,Flutter returns an overflow warning:To learn how constraints work in Flutter,see Understanding constraints.<topic_end><topic_start>Design systemBecause Flutter targets multiple platforms, your app doesn’t needto conform to any design system.Though this guide features Material widgets,your Flutter app can use many different design systems:If you’re looking for a great reference app that features acustom design system, check out Wonderous.<topic_end><topic_start>UI BasicsThis section covers the basics of UI development inFlutter and how it compares to SwiftUI.This includes how to start developing your app, display static text,create buttons, react to on-press events, display lists, grids, and more.<topic_end><topic_start>Getting startedIn SwiftUI, you use App to start your app.Another common SwiftUI practice places the app body within a structthat conforms to the View protocol as follows:To start your Flutter app, pass in an instance of your app tothe runApp function.<code_start>void main() {  runApp(const MyApp());}<code_end>App is a widget. The build method describes the part of theuser interface it represents.It’s common to begin your app with a WidgetApp class,like CupertinoApp.<code_start>class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    // Returns a CupertinoApp that, by default,    // has the look and feel of an iOS app.    return const CupertinoApp(      home: HomePage(),    );  }}<code_end>The widget used in HomePage might begin with the Scaffold class.Scaffold implements a basic layout structure for an app.<code_start>class HomePage extends StatelessWidget {  const HomePage({super.key});  @override  Widget build(BuildContext context) {    return const Scaffold(      body: Center(        child: Text(          'Hello, World!',        ),      ),    );  }}<code_end>Note how Flutter uses the Center widget.SwiftUI renders a view’s contents in its center by default.That’s not always the case with Flutter.Scaffold doesn’t render its body widget at the center of the screen.To center the text, wrap it in a Center widget.To learn about different widgets and their default behaviors, check outthe Widget catalog.<topic_end><topic_start>Adding ButtonsIn SwiftUI, you use the Button struct to create a button.To achieve the same result in Flutter,use the CupertinoButton class:<code_start>        CupertinoButton(  onPressed: () {    // This closure is called when your button is tapped.  },  child: const Text('Do something'),)<code_end>Flutter gives you access to a variety of buttons with predefined styles.The CupertinoButton class comes from the Cupertino library.Widgets in the Cupertino library use Apple’s design system.<topic_end><topic_start>Aligning components horizontallyIn SwiftUI, stack views play a big part in designing your layouts.Two separate structures allow you to create stacks:HStack for horizontal stack viewsVStack for vertical stack viewsThe following SwiftUI view adds a globe image andtext to a horizontal stack view:Flutter uses Row rather than HStack:<code_start>    Row(  mainAxisAlignment: MainAxisAlignment.center,  children: [    Icon(CupertinoIcons.globe),    Text('Hello, world!'),  ],),<code_end>The Row widget requires a List<Widget> in the children parameter.The mainAxisAlignment property tells Flutter how to position childrenwith extra space. MainAxisAlignment.center positions children in thecenter of the main axis. For Row, the main axis is the horizontalaxis.<topic_end><topic_start>Aligning components verticallyThe following examples build on those in the previous section.In SwiftUI, you use VStack to arrange the components into avertical pillar.Flutter uses the same Dart code from the previous example,except it swaps Column for Row:<code_start>    Column(  mainAxisAlignment: MainAxisAlignment.center,  children: [    Icon(CupertinoIcons.globe),    Text('Hello, world!'),  ],),<code_end><topic_end><topic_start>Displaying a list viewIn SwiftUI, you use the List base component to display sequencesof items.To display a sequence of model objects, make sure that the user canidentify your model objects.To make an object identifiable, use the Identifiable protocol.This resembles how Flutter prefers to build its list widgets.Flutter doesn’t need the list items to be identifiable.You set the number of items to display then build a widget for each item.<code_start>class Person {  String name;  Person(this.name);}var items = [  Person('Person 1'),  Person('Person 2'),  Person('Person 3'),];class HomePage extends StatelessWidget {  const HomePage({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      body: ListView.builder(        itemCount: items.length,        itemBuilder: (context, index) {          return ListTile(            title: Text(items[index].name),          );        },      ),    );  }}<code_end>Flutter has some caveats for lists:The ListView widget has a builder method.This works like the ForEach within SwiftUI’s List struct.The itemCount parameter of the ListView sets how many itemsthe ListView displays.The itemBuilder has an index parameter that will be between zeroand one less than itemCount.The previous example returned a ListTile widget for each item.The ListTile widget includes properties like height and font-size.These properties help build a list. However, Flutter allows you to returnalmost any widget that represents your data.<topic_end><topic_start>Displaying a gridWhen constructing non-conditional grids in SwiftUI,you use Grid with GridRow.To display grids in Flutter, use the GridView widget.This widget has various constructors. Each constructor hasa similar goal, but uses different input parameters.The following example uses the .builder() initializer:<code_start>const widgets = [  Text('Row 1'),  Icon(CupertinoIcons.arrow_down_square),  Icon(CupertinoIcons.arrow_up_square),  Text('Row 2'),  Icon(CupertinoIcons.arrow_down_square),  Icon(CupertinoIcons.arrow_up_square),];class HomePage extends StatelessWidget {  const HomePage({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      body: GridView.builder(        gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(          crossAxisCount: 3,          mainAxisExtent: 40,        ),        itemCount: widgets.length,        itemBuilder: (context, index) => widgets[index],      ),    );  }}<code_end>The SliverGridDelegateWithFixedCrossAxisCount delegate determinesvarious parameters that the grid uses to lay out its components.This includes crossAxisCount that dictates the number of itemsdisplayed on each row.How SwiftUI’s Grid and Flutter’s GridView differ in that Gridrequires GridRow. GridView uses the delegate to decide how thegrid should lay out its components.<topic_end><topic_start>Creating a scroll viewIn SwiftUI, you use ScrollView to create custom scrollingcomponents.The following example displays a series of PersonView instancesin a scrollable fashion.To create a scrolling view, Flutter uses SingleChildScrollView.In the following example, the function mockPerson mocks instancesof the Person class to create the custom PersonView widget.<code_start>    SingleChildScrollView(  child: Column(    children: mockPersons        .map(          (person) => PersonView(            person: person,          ),        )        .toList(),  ),),<code_end><topic_end><topic_start>Responsive and adaptive designIn SwiftUI, you use GeometryReader to create relative view sizes.For example, you could:You can also see if the size class has .regular or .compactusing horizontalSizeClass.To create relative views in Flutter, you can use one of two options:To learn more, check out Creating responsive and adaptive apps.<topic_end><topic_start>Managing stateIn SwiftUI, you use the @State property wrapper to represent theinternal state of a SwiftUI view.SwiftUI also includes several options for more complex statemanagement such as the ObservableObject protocol.Flutter manages local state using a StatefulWidget.Implement a stateful widget with the following two classes:The State object stores the widget’s state.To change a widget’s state, call setState() from the State subclassto tell the framework to redraw the widget.The following example shows a part of a counter app:<code_start>class MyHomePage extends StatefulWidget {  const MyHomePage({super.key});  @override  State<MyHomePage> createState() => _MyHomePageState();}class _MyHomePageState extends State<MyHomePage> {  int _counter = 0;  @override  Widget build(BuildContext context) {    return Scaffold(      body: Center(        child: Column(          mainAxisAlignment: MainAxisAlignment.center,          children: [            Text('$_counter'),            TextButton(              onPressed: () => setState(() {                _counter++;              }),              child: const Text('+'),            ),          ],        ),      ),    );  }}<code_end>To learn more ways to manage state, check out State management.<topic_end><topic_start>AnimationsTwo main types of UI animations exist.<topic_end><topic_start>Implicit AnimationSwiftUI and Flutter take a similar approach to animation.In both frameworks, you specify parameters like duration, and curve.In SwiftUI, you use the animate() modifier to handle implicitanimation.Flutter includes widgets for implicit animation.This simplifies animating common widgets.Flutter names these widgets with the following format: AnimatedFoo.For example: To rotate a button, use the AnimatedRotation class.This animates the Transform.rotate widget.<code_start>    AnimatedRotation(  duration: const Duration(seconds: 1),  turns: turns,  curve: Curves.easeIn,  child: TextButton(      onPressed: () {        setState(() {          turns += .125;        });      },      child: const Text('Tap me!')),),<code_end>Flutter allows you to create custom implicit animations.To compose a new animated widget, use the TweenAnimationBuilder.<topic_end><topic_start>Explicit AnimationFor explicit animations, SwiftUI uses the withAnimation() function.Flutter includes explicitly animated widgets with names formattedlike FooTransition.One example would be the RotationTransition class.Flutter also allows you to create a custom explicit animation usingAnimatedWidget or AnimatedBuilder.To learn more about animations in Flutter, see Animations overview.<topic_end><topic_start>Drawing on the ScreenIn SwiftUI, you use CoreGraphics to draw lines and shapes to thescreen.Flutter has an API based on the Canvas class,with two classes that help you draw:CustomPaint that requires a painter:<code_start>    CustomPaint(   painter: SignaturePainter(_points),   size: Size.infinite, ),<code_end>CustomPainter that implements your algorithm to draw to the canvas.<code_start>class SignaturePainter extends CustomPainter {   SignaturePainter(this.points);   final List<Offset?> points;   @override   void paint(Canvas canvas, Size size) {     final Paint paint = Paint()       ..color = Colors.black       ..strokeCap = StrokeCap.round       ..strokeWidth = 5;     for (int i = 0; i < points.length - 1; i++) {       if (points[i] != null && points[i + 1] != null) {         canvas.drawLine(points[i]!, points[i + 1]!, paint);       }     }   }   @override   bool shouldRepaint(SignaturePainter oldDelegate) =>       oldDelegate.points != points; }<code_end><topic_end><topic_start>NavigationThis section explains how to navigate between pages of an app,the push and pop mechanism, and more.<topic_end><topic_start>Navigating between pagesDevelopers build iOS and macOS apps with different pages callednavigation routes.In SwiftUI, the NavigationStack represents this stack of pages.The following example creates an app that displays a list of persons.To display a person’s details in a new navigation link,tap on that person.If you have a small Flutter app without complex linking,use Navigator with named routes.After defining your navigation routes,call your navigation routes using their names.Name each route in the class passed to the runApp() function.The following example uses App:<code_start>// Defines the route name as a constant // so that it's reusable. const detailsPageRouteName = '/details'; class App extends StatelessWidget {   const App({     super.key,   });   @override   Widget build(BuildContext context) {     return CupertinoApp(       home: const HomePage(),       // The [routes] property defines the available named routes       // and the widgets to build when navigating to those routes.       routes: {         detailsPageRouteName: (context) => const DetailsPage(),       },     );   } }<code_end>The following sample generates a list of persons usingmockPersons(). Tapping a person pushes the person’s detail pageto the Navigator using pushNamed().<code_start>    ListView.builder(   itemCount: mockPersons.length,   itemBuilder: (context, index) {     final person = mockPersons.elementAt(index);     final age = '${person.age} years old';     return ListTile(       title: Text(person.name),       subtitle: Text(age),       trailing: const Icon(         Icons.arrow_forward_ios,       ),       onTap: () {         // When a [ListTile] that represents a person is         // tapped, push the detailsPageRouteName route         // to the Navigator and pass the person's instance         // to the route.         Navigator.of(context).pushNamed(           detailsPageRouteName,           arguments: person,         );       },     );   }, ),<code_end>Define the DetailsPage widget that displays the details ofeach person. In Flutter, you can pass arguments into thewidget when navigating to the new route.Extract the arguments using ModalRoute.of():<code_start>class DetailsPage extends StatelessWidget {   const DetailsPage({super.key});   @override   Widget build(BuildContext context) {     // Read the person instance from the arguments.     final Person person = ModalRoute.of(       context,     )?.settings.arguments as Person;     // Extract the age.     final age = '${person.age} years old';     return Scaffold(       // Display name and age.       body: Column(children: [Text(person.name), Text(age)]),     );   } }<code_end>To create more advanced navigation and routing requirements,use a routing package such as go_router.To learn more, check out Navigation and routing.<topic_end><topic_start>Manually pop backIn SwiftUI, you use the dismiss environment value to pop-back tothe previous screen.In Flutter, use the pop() function of the Navigator class:<code_start>TextButton(  onPressed: () {    // This code allows the    // view to pop back to its presenter.    Navigator.of(context).pop();  },  child: const Text('Pop back'),),<code_end><topic_end><topic_start>Navigating to another appIn SwiftUI, you use the openURL environment variable to open aURL to another application.In Flutter, use the url_launcher plugin.<code_start>    CupertinoButton(  onPressed: () async {    await launchUrl(      Uri.parse('https://google.com'),    );  },  child: const Text(    'Open website',  ),),<code_end><topic_end><topic_start>Themes, styles, and mediaYou can style Flutter apps with little effort.Styling includes switching between light and dark themes,changing the design of your text and UI components,and more. This section covers how to style your apps.<topic_end><topic_start>Using dark modeIn SwiftUI, you call the preferredColorScheme()function on a View to use dark mode.In Flutter, you can control light and dark mode at the app-level.To control the brightness mode, use the theme propertyof the App class:<code_start>    CupertinoApp(  theme: CupertinoThemeData(    brightness: Brightness.dark,  ),  home: HomePage(),);<code_end><topic_end><topic_start>Styling textIn SwiftUI, you use modifier functions to style text.For example, to change the font of a Text string,use the font() modifier:To style text in Flutter, add a TextStyle widget as the valueof the style parameter of the Text widget.<code_start>    Text(  'Hello, world!',  style: TextStyle(    fontSize: 30,    fontWeight: FontWeight.bold,    color: CupertinoColors.systemYellow,  ),),<code_end><topic_end><topic_start>Styling buttonsIn SwiftUI, you use modifier functions to style buttons.To style button widgets in Flutter, set the style of its child,or modify properties on the button itself.In the following example:<code_start>child: CupertinoButton(  color: CupertinoColors.systemYellow,  onPressed: () {},  padding: const EdgeInsets.all(16),  child: const Text(    'Do something',    style: TextStyle(      color: CupertinoColors.systemBlue,      fontSize: 30,      fontWeight: FontWeight.bold,    ),  ),),<code_end><topic_end><topic_start>Using custom fontsIn SwiftUI, you can use a custom font in your app in two steps.First, add the font file to your SwiftUI project. After adding the file,use the .font() modifier to apply it to your UI components.In Flutter, you control your resources with a filenamed pubspec.yaml. This file is platform agnostic.To add a custom font to your project, follow these steps:Add your custom font(s) under the fonts section.After you add the font to your project, you can use it as in thefollowing example:<code_start>        Text(  'Cupertino',  style: TextStyle(    fontSize: 40,    fontFamily: 'BungeeSpice',  ),)<code_end>info Note  To download custom fonts to use in your apps,  check out Google Fonts.<topic_end><topic_start>Bundling images in appsIn SwiftUI, you first add the image files to Assets.xcassets,then use the Image view to display the images.To add images in Flutter, follow a method similar to how you addedcustom fonts.Add this asset to the pubspec.yaml file.After adding your image, display it using the Image widget’s.asset() constructor. This constructor:To review a complete example, check out the Image docs.<topic_end><topic_start>Bundling videos in appsIn SwiftUI, you bundle a local video file with your app in twosteps.First, you import the AVKit framework, then you instantiate aVideoPlayer view.In Flutter, add the video_player plugin to your project.This plugin allows you to create a video player that works onAndroid, iOS, and on the web from the same codebase.To review a complete walkthrough, check out the video_player example.<topic_end><topic_start>Flutter for UIKit developersiOS developers with experience using UIKitwho want to write mobile apps using Fluttershould review this guide.It explains how to apply existing UIKit knowledge to Flutter.info Note  If you have experience building apps with SwiftUI,  check out Flutter for SwiftUI developers instead.Flutter is a framework for building cross-platform applicationsthat uses the Dart programming language.To understand some differences between programming with Dartand programming with Swift, see Learning Dart as a Swift Developerand Flutter concurrency for Swift developers.Your iOS and UIKit knowledge and experienceare highly valuable when building with Flutter.Flutter also makes a number of adaptationsto app behavior when running on iOS.To learn how, see Platform adaptations.info  To integrate Flutter code into an existing iOS app,  check out Add Flutter to existing app.Use this guide as a cookbook.Jump around and find questions that address your most relevant needs.<topic_end><topic_start>OverviewAs an introduction, watch the following video.It outlines how Flutter works on iOS and how to use Flutter to build iOS apps.<topic_end><topic_start>Views vs. WidgetsHow is react-style, or declarative,  programming different from the  traditional imperative style?  For a comparison, see Introduction to declarative UI.In UIKit, most of what you create in the UI is done using view objects,which are instances of the UIView class.These can act as containers for other UIView classes,which form your layout.In Flutter, the rough equivalent to a UIView is a Widget.Widgets don’t map exactly to iOS views,but while you’re getting acquainted with how Flutter worksyou can think of them as “the way you declare and construct UI”.However, these have a few differences to a UIView.To start, widgets have a different lifespan: they are immutableand only exist until they need to be changed.Whenever widgets or their state change,Flutter’s framework creates a new tree of widget instances.In comparison, a UIKit view is not recreated when it changes,but rather it’s a mutable entity that is drawn onceand doesn’t redraw until it is invalidated using setNeedsDisplay().Furthermore, unlike UIView, Flutter’s widgets are lightweight,in part due to their immutability.Because they aren’t views themselves,and aren’t directly drawing anything,but rather are a description of the UI and its semanticsthat get “inflated” into actual view objects under the hood.Flutter includes the Material Components library.These are widgets that implement theMaterial Design guidelines.Material Design is a flexible design systemoptimized for all platforms, including iOS.But Flutter is flexible and expressive enoughto implement any design language.On iOS, you can use the Cupertino widgetsto produce an interface that looks likeApple’s iOS design language.<topic_end><topic_start>Updating WidgetsTo update your views in UIKit, you directly mutate them.In Flutter, widgets are immutable and not updated directly.Instead, you have to manipulate the widget’s state.This is where the concept of Stateful vs Stateless widgetscomes in. A StatelessWidget is just what it soundslike—a widget with no state attached.StatelessWidgets are useful when the part of the user interface you aredescribing does not depend on anything other than the initial configurationinformation in the widget.For example, with UIKit, this is similar to placing a UIImageViewwith your logo as the image. If the logo is not changing during runtime,use a StatelessWidget in Flutter.If you want to dynamically change the UI based on data receivedafter making an HTTP call, use a StatefulWidget.After the HTTP call has completed, tell the Flutter frameworkthat the widget’s State is updated, so it can update the UI.The important difference between stateless andstateful widgets is that StatefulWidgets have a State objectthat stores state data and carries it over across tree rebuilds,so it’s not lost.If you are in doubt, remember this rule:if a widget changes outside of the build method(because of runtime user interactions, for example),it’s stateful.If the widget never changes, once built, it’s stateless.However, even if a widget is stateful, the containing parent widgetcan still be stateless if it isn’t itself reacting to those changes(or other inputs).The following example shows how to use a StatelessWidget.A commonStatelessWidget is the Text widget.If you look at the implementation of the Text widget,you’ll find it subclasses StatelessWidget.<code_start>Text(  'I like Flutter!',  style: TextStyle(fontWeight: FontWeight.bold),);<code_end>If you look at the code above, you might notice that the Text widgetcarries no explicit state with it. It renders what is passed in itsconstructors and nothing more.But, what if you want to make “I Like Flutter” change dynamically,for example when clicking a FloatingActionButton?To achieve this, wrap the Text widget in a StatefulWidget andupdate it when the user clicks the button.For example:<code_start>class SampleApp extends StatelessWidget {  // This widget is the root of your application.  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  // Default placeholder text  String textToShow = 'I Like Flutter';  void _updateText() {    setState(() {      // Update the text      textToShow = 'Flutter is Awesome!';    });  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Sample App')),      body: Center(child: Text(textToShow)),      floatingActionButton: FloatingActionButton(        onPressed: _updateText,        tooltip: 'Update Text',        child: const Icon(Icons.update),      ),    );  }}<code_end><topic_end><topic_start>Widget layoutIn UIKit, you might use a Storyboard fileto organize your views and set constraints,or you might set your constraints programmatically in your view controllers.In Flutter, declare your layout in code by composing a widget tree.The following example shows how to display a simple widget with padding:<code_start>@overrideWidget build(BuildContext context) {  return Scaffold(    appBar: AppBar(title: const Text('Sample App')),    body: Center(      child: CupertinoButton(        onPressed: () {},        padding: const EdgeInsets.only(left: 10, right: 10),        child: const Text('Hello'),      ),    ),  );}<code_end>You can add padding to any widget,which mimics the functionality of constraints in iOS.You can view the layouts that Flutter has to offerin the widget catalog.<topic_end><topic_start>Removing WidgetsIn UIKit, you call addSubview() on the parent,or removeFromSuperview() on a child viewto dynamically add or remove child views.In Flutter, because widgets are immutable,there is no direct equivalent to addSubview().Instead, you can pass a function to the parentthat returns a widget, and control that child’s creationwith a boolean flag.The following example shows how to toggle between two widgetswhen the user clicks the FloatingActionButton:<code_start>class SampleApp extends StatelessWidget {  // This widget is the root of your application.  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  // Default value for toggle.  bool toggle = true;  void _toggle() {    setState(() {      toggle = !toggle;    });  }  Widget _getToggleChild() {    if (toggle) {      return const Text('Toggle One');    }    return CupertinoButton(      onPressed: () {},      child: const Text('Toggle Two'),    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: Center(        child: _getToggleChild(),      ),      floatingActionButton: FloatingActionButton(        onPressed: _toggle,        tooltip: 'Update Text',        child: const Icon(Icons.update),      ),    );  }}<code_end><topic_end><topic_start>AnimationsIn UIKit, you create an animation by calling theanimate(withDuration:animations:) method on a view.In Flutter, use the animation libraryto wrap widgets inside an animated widget.In Flutter, use an AnimationController, which is an Animation<double>that can pause, seek, stop, and reverse the animation.It requires a Ticker that signals when vsync happensand produces a linear interpolationbetween 0 and 1 on each frame while it’s running.You then create one or moreAnimations and attach them to the controller.For example, you might use CurvedAnimationto implement an animation along an interpolated curve.In this sense, the controller is the “master” sourceof the animation progressand the CurvedAnimation computes the curvethat replaces the controller’s default linear motion.Like widgets, animations in Flutter work with composition.When building the widget tree you assign the Animation to an animatedproperty of a widget, such as the opacity of a FadeTransition,and tell the controller to start the animation.The following example shows how to write a FadeTransition thatfades the widget into a logo when you press the FloatingActionButton:<code_start>import 'package:flutter/material.dart';class SampleApp extends StatelessWidget {  // This widget is the root of your application.  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Fade Demo',      home: MyFadeTest(title: 'Fade Demo'),    );  }}class MyFadeTest extends StatefulWidget {  const MyFadeTest({super.key, required this.title});  final String title;  @override  State<MyFadeTest> createState() => _MyFadeTest();}class _MyFadeTest extends State<MyFadeTest>    with SingleTickerProviderStateMixin {  late AnimationController controller;  late CurvedAnimation curve;  @override  void initState() {    super.initState();    controller = AnimationController(      duration: const Duration(milliseconds: 2000),      vsync: this,    );    curve = CurvedAnimation(      parent: controller,      curve: Curves.easeIn,    );  }  @override  void dispose() {    controller.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: Text(widget.title)),      body: Center(        child: FadeTransition(          opacity: curve,          child: const FlutterLogo(size: 100),        ),      ),      floatingActionButton: FloatingActionButton(        onPressed: () {          controller.forward();        },        tooltip: 'Fade',        child: const Icon(Icons.brush),      ),    );  }}<code_end>For more information, see Animation & Motion widgets,the Animations tutorial, and the Animations overview.<topic_end><topic_start>Drawing on the screenIn UIKit, you use CoreGraphics to draw lines and shapes to thescreen. Flutter has a different API based on the Canvas class,with two other classes that help you draw: CustomPaint and CustomPainter,the latter of which implements your algorithm to draw to the canvas.To learn how to implement a signature painter in Flutter,see Collin’s answer on StackOverflow.<code_start>import 'package:flutter/material.dart';void main() => runApp(const MaterialApp(home: DemoApp()));class DemoApp extends StatelessWidget {  const DemoApp({super.key});  @override  Widget build(BuildContext context) => const Scaffold(body: Signature());}class Signature extends StatefulWidget {  const Signature({super.key});  @override  State<Signature> createState() => SignatureState();}class SignatureState extends State<Signature> {  List<Offset?> _points = <Offset?>[];  @override  Widget build(BuildContext context) {    return GestureDetector(      onPanUpdate: (details) {        setState(() {          RenderBox? referenceBox = context.findRenderObject() as RenderBox;          Offset localPosition =              referenceBox.globalToLocal(details.globalPosition);          _points = List.from(_points)..add(localPosition);        });      },      onPanEnd: (details) => _points.add(null),      child:          CustomPaint(        painter: SignaturePainter(_points),        size: Size.infinite,      ),    );  }}class SignaturePainter extends CustomPainter {  SignaturePainter(this.points);  final List<Offset?> points;  @override  void paint(Canvas canvas, Size size) {    final Paint paint = Paint()      ..color = Colors.black      ..strokeCap = StrokeCap.round      ..strokeWidth = 5;    for (int i = 0; i < points.length - 1; i++) {      if (points[i] != null && points[i + 1] != null) {        canvas.drawLine(points[i]!, points[i + 1]!, paint);      }    }  }  @override  bool shouldRepaint(SignaturePainter oldDelegate) =>      oldDelegate.points != points;}<code_end><topic_end><topic_start>Widget opacityIn UIKit, everything has .opacity or .alpha.In Flutter, most of the time you need towrap a widget in an Opacity widget to accomplish this.<topic_end><topic_start>Custom WidgetsIn UIKit, you typically subclass UIView, or use a pre-existing view,to override and implement methods that achieve the desired behavior.In Flutter, build a custom widget by composing smaller widgets(instead of extending them).For example, how do you build a CustomButtonthat takes a label in the constructor?Create a CustomButton that composes a ElevatedButton with a label,rather than by extending ElevatedButton:<code_start>class CustomButton extends StatelessWidget {  const CustomButton(this.label, {super.key});  final String label;  @override  Widget build(BuildContext context) {    return ElevatedButton(      onPressed: () {},      child: Text(label),    );  }}<code_end>Then use CustomButton,just as you’d use any other Flutter widget:<code_start>@overrideWidget build(BuildContext context) {  return const Center(    child: CustomButton('Hello'),  );}<code_end><topic_end><topic_start>NavigationThis section of the document discusses navigationbetween pages of an app, the push and pop mechanism, and more.<topic_end><topic_start>Navigating between pagesIn UIKit, to travel between view controllers, you can use aUINavigationController that manages the stack of view controllersto display.Flutter has a similar implementation,using a Navigator and Routes.A Route is an abstraction for a “screen” or “page” of an app,and a Navigator is a widgetthat manages routes. A route roughly maps to aUIViewController. The navigator works in a similar way to the iOSUINavigationController, in that it can push() and pop()routes depending on whether you want to navigate to, or back from, a view.To navigate between pages, you have a couple options:The following example builds a Map.<code_start>void main() {  runApp(    CupertinoApp(      home: const MyAppHome(), // becomes the route named '/'      routes: <String, WidgetBuilder>{        '/a': (context) => const MyPage(title: 'page A'),        '/b': (context) => const MyPage(title: 'page B'),        '/c': (context) => const MyPage(title: 'page C'),      },    ),  );}<code_end>Navigate to a route by pushing its name to the Navigator.<code_start>Navigator.of(context).pushNamed('/b');<code_end>The Navigator class handles routing in Flutter and is used to geta result back from a route that you have pushed on the stack.This is done by awaiting on the Future returned by push().For example, to start a location route that lets the user select theirlocation, you might do the following:<code_start>Object? coordinates = await Navigator.of(context).pushNamed('/location');<code_end>And then, inside your location route, once the user has selected theirlocation, pop() the stack with the result:<code_start>Navigator.of(context).pop({'lat': 43.821757, 'long': -79.226392});<code_end><topic_end><topic_start>Navigating to another appIn UIKit, to send the user to another application,you use a specific URL scheme.For the system level apps, the scheme depends on the app.To implement this functionality in Flutter,create a native platform integration, or use anexisting plugin, such as url_launcher.<topic_end><topic_start>Manually pop backCalling SystemNavigator.pop() from your Dart codeinvokes the following iOS code:If that doesn’t do what you want, you can create your ownplatform channel to invoke arbitrary iOS code.<topic_end><topic_start>Handling localizationUnlike iOS, which has the Localizable.strings file,Flutter doesn’t currently have a dedicated system for handling strings.At the moment, the best practice is to declare your copy textin a class as static fields and access them from there. For example:<code_start>class Strings {  static const String welcomeMessage = 'Welcome To Flutter';}<code_end>You can access your strings as such:<code_start>Text(Strings.welcomeMessage);<code_end>By default, Flutter only supports US English for its strings.If you need to add support for other languages,include the flutter_localizations package.You might also need to add Dart’s intlpackage to use i10n machinery, such as date/time formatting.To use the flutter_localizations package,specify the localizationsDelegates andsupportedLocales on the app widget:<code_start>import 'package:flutter/material.dart';import 'package:flutter_localizations/flutter_localizations.dart';class MyWidget extends StatelessWidget {  const MyWidget({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      localizationsDelegates: <LocalizationsDelegate<dynamic>>[        // Add app-specific localization delegate[s] here        GlobalMaterialLocalizations.delegate,        GlobalWidgetsLocalizations.delegate,      ],      supportedLocales: <Locale>[        Locale('en', 'US'), // English        Locale('he', 'IL'), // Hebrew        // ... other locales the app supports      ],    );  }}<code_end>The delegates contain the actual localized values,while the supportedLocales defines which locales the app supports.The above example uses a MaterialApp,so it has both a GlobalWidgetsLocalizationsfor the base widgets localized values,and a MaterialWidgetsLocalizations for the Material widgets localizations.If you use WidgetsApp for your app, you don’t need the latter.Note that these two delegates contain “default” values,but you’ll need to provide one or more delegatesfor your own app’s localizable copy,if you want those to be localized too.When initialized, the WidgetsApp (or MaterialApp)creates a Localizations widget for you,with the delegates you specify.The current locale for the device is always accessiblefrom the Localizations widget from the current context(in the form of a Locale object), or using the Window.locale.To access localized resources, use the Localizations.of() methodto access a specific localizations class that is provided by a given delegate.Use the intl_translation package to extract translatable copyto arb files for translating, and importing them back into the appfor using them with intl.For further details on internationalization and localization in Flutter,see the internationalization guide, which has sample codewith and without the intl package.<topic_end><topic_start>Managing dependenciesIn iOS, you add dependencies with CocoaPods by adding to your Podfile.Flutter uses Dart’s build system and the Pub package managerto handle dependencies. The tools delegate the building of thenative Android and iOS wrapper apps to therespective build systems.While there is a Podfile in the iOS folder in yourFlutter project, only use this if you are adding nativedependencies needed for per-platform integration.In general, use pubspec.yaml to declare external dependencies in Flutter.A good place to find great packages for Flutter is on pub.dev.<topic_end><topic_start>ViewControllersThis section of the document discusses the equivalentof ViewController in Flutter and how to listen tolifecycle events.<topic_end><topic_start>Equivalent of ViewController in FlutterIn UIKit, a ViewController represents a portion of user interface,most commonly used for a screen or section.These are composed together to build complex user interfaces,and help scale your application’s UI.In Flutter, this job falls to Widgets.As mentioned in the Navigation section,screens in Flutter are represented by Widgets since“everything is a widget!”Use a Navigator to move between different Routesthat represent different screens or pages,or maybe different states or renderings of the same data.<topic_end><topic_start>Listening to lifecycle eventsIn UIKit, you can override methods to the ViewControllerto capture lifecycle methods for the view itself,or register lifecycle callbacks in the AppDelegate.In Flutter, you have neither concept, but you can insteadlisten to lifecycle events by hooking intothe WidgetsBinding observer and listening tothe didChangeAppLifecycleState() change event.The observable lifecycle events are:For more details on the meaning of these states, seeAppLifecycleState documentation.<topic_end><topic_start>LayoutsThis section discusses different layouts in Flutterand how they compare with UIKit.<topic_end><topic_start>Displaying a list viewIn UIKit, you might show a list ineither a UITableView or a UICollectionView.In Flutter, you have a similar implementation using a ListView.In UIKit, these views have delegate methodsfor deciding the number of rows,the cell for each index path, and the size of the cells.Due to Flutter’s immutable widget pattern,you pass a list of widgets to your ListView,and Flutter takes care of making sure thatscrolling is fast and smooth.<code_start>import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List<Widget> _getListData() {    final List<Widget> widgets = [];    for (int i = 0; i < 100; i++) {      widgets.add(Padding(        padding: const EdgeInsets.all(10),        child: Text('Row $i'),      ));    }    return widgets;  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: ListView(children: _getListData()),    );  }}<code_end><topic_end><topic_start>Detecting what was clickedIn UIKit, you implement the delegate method,tableView:didSelectRowAtIndexPath:.In Flutter, use the touch handling provided by the passed-in widgets.<code_start>import 'dart:developer' as developer;import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List<Widget> _getListData() {    List<Widget> widgets = [];    for (int i = 0; i < 100; i++) {      widgets.add(        GestureDetector(          onTap: () {            developer.log('row tapped');          },          child: Padding(            padding: const EdgeInsets.all(10),            child: Text('Row $i'),          ),        ),      );    }    return widgets;  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: ListView(children: _getListData()),    );  }}<code_end><topic_end><topic_start>Dynamically updating ListViewIn UIKit, you update the data for the list view,and notify the table or collection view using thereloadData method.In Flutter, if you update the list of widgets inside a setState(),you quickly see that your data doesn’t change visually.This is because when setState() is called,the Flutter rendering engine looks at the widget treeto see if anything has changed.When it gets to your ListView, it performs an == check,and determines that the two ListViews are the same.Nothing has changed, so no update is required.For a simple way to update your ListView,create a new List inside of setState(),and copy the data from the old list to the new list.While this approach is simple,it is not recommended for large data sets,as shown in the next example.<code_start>import 'dart:developer' as developer;import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List<Widget> widgets = <Widget>[];  @override  void initState() {    super.initState();    for (int i = 0; i < 100; i++) {      widgets.add(getRow(i));    }  }  Widget getRow(int i) {    return GestureDetector(      onTap: () {        setState(() {          widgets = List.from(widgets);          widgets.add(getRow(widgets.length));          developer.log('row $i');        });      },      child: Padding(        padding: const EdgeInsets.all(10),        child: Text('Row $i'),      ),    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: ListView(children: widgets),    );  }}<code_end>The recommended, efficient,and effective way to build a list uses a ListView.Builder.This method is great when you have a dynamiclist or a list with very large amounts of data.<code_start>import 'dart:developer' as developer;import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List<Widget> widgets = [];  @override  void initState() {    super.initState();    for (int i = 0; i < 100; i++) {      widgets.add(getRow(i));    }  }  Widget getRow(int i) {    return GestureDetector(      onTap: () {        setState(() {          widgets.add(getRow(widgets.length));          developer.log('row $i');        });      },      child: Padding(        padding: const EdgeInsets.all(10),        child: Text('Row $i'),      ),    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: ListView.builder(        itemCount: widgets.length,        itemBuilder: (context, position) {          return getRow(position);        },      ),    );  }}<code_end>Instead of creating a ListView, create a ListView.builderthat takes two key parameters: the initial length of the list,and an ItemBuilder function.The ItemBuilder function is similar to the cellForItemAtdelegate method in an iOS table or collection view,as it takes a position, and returns thecell you want rendered at that position.Finally, but most importantly, notice that the onTap() functiondoesn’t recreate the list anymore, but instead .adds to it.<topic_end><topic_start>Creating a scroll viewIn UIKit, you wrap your views in a ScrollView thatallows a user to scroll your content if needed.In Flutter the easiest way to do this is using the ListView widget.This acts as both a ScrollView and an iOS TableView,as you can lay out widgets in a vertical format.<code_start>@overrideWidget build(BuildContext context) {  return ListView(    children: const <Widget>[      Text('Row One'),      Text('Row Two'),      Text('Row Three'),      Text('Row Four'),    ],  );}<code_end>For more detailed docs on how to lay out widgets in Flutter,see the layout tutorial.<topic_end><topic_start>Gesture detection and touch event handlingThis section discusses how to detect gesturesand handle different events in Flutter,and how they compare with UIKit.<topic_end><topic_start>Adding a click listenerIn UIKit, you attach a GestureRecognizer to a view tohandle click events.In Flutter, there are two ways of adding touch listeners:If the widget supports event detection, pass a function to it,and handle the event in the function. For example, theElevatedButton widget has an onPressed parameter:<code_start>@overrideWidget build(BuildContext context) {  return ElevatedButton(    onPressed: () {      developer.log('click');    },    child: const Text('Button'),  );}<code_end>If the Widget doesn’t support event detection,wrap the widget in a GestureDetector and pass a functionto the onTap parameter.<code_start>class SampleTapApp extends StatelessWidget {  const SampleTapApp({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      body: Center(        child: GestureDetector(          onTap: () {            developer.log('tap');          },          child: const FlutterLogo(            size: 200,          ),        ),      ),    );  }}<code_end><topic_end><topic_start>Handling other gesturesUsing GestureDetector you can listento a wide range of gestures such as:TappingDouble tappingLong pressingVertical draggingHorizontal draggingThe following example shows a GestureDetectorthat rotates the Flutter logo on a double tap:<code_start>class SampleApp extends StatefulWidget {  const SampleApp({super.key});  @override  State<SampleApp> createState() => _SampleAppState();}class _SampleAppState extends State<SampleApp>    with SingleTickerProviderStateMixin {  late AnimationController controller;  late CurvedAnimation curve;  @override  void initState() {    super.initState();    controller = AnimationController(      vsync: this,      duration: const Duration(milliseconds: 2000),    );    curve = CurvedAnimation(      parent: controller,      curve: Curves.easeIn,    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      body: Center(        child: GestureDetector(          onDoubleTap: () {            if (controller.isCompleted) {              controller.reverse();            } else {              controller.forward();            }          },          child: RotationTransition(            turns: curve,            child: const FlutterLogo(              size: 200,            ),          ),        ),      ),    );  }}<code_end><topic_end><topic_start>Themes, styles, and mediaFlutter applications are easy to style; you can switchbetween light and dark themes,change the style of your text and UI components,and more. This section covers aspects of styling your Flutter appsand compares how you might do the same in UIKit.<topic_end><topic_start>Using a themeOut of the box, Flutter comes with a beautiful implementationof Material Design, which takes care of a lot of styling andtheming needs that you would typically do.To take full advantage of Material Components in your app,declare a top-level widget, MaterialApp,as the entry point to your application.MaterialApp is a convenience widget that wraps a numberof widgets that are commonly required for applicationsimplementing Material Design.It builds upon a WidgetsApp by adding Material specific functionality.But Flutter is flexible and expressive enough to implementany design language. On iOS, you can use theCupertino library to produce an interface that adheres to theHuman Interface Guidelines.For the full set of these widgets,see the Cupertino widgets gallery.You can also use a WidgetsApp as your app widget,which provides some of the same functionality,but is not as rich as MaterialApp.To customize the colors and styles of any child components,pass a ThemeData object to the MaterialApp widget.For example, in the code below,the color scheme from seed is set to deepPurple and divider color is grey.<code_start>import 'package:flutter/material.dart';class SampleApp extends StatelessWidget {  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Sample App',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),        dividerColor: Colors.grey,      ),      home: const SampleAppPage(),    );  }}<code_end><topic_end><topic_start>Using custom fontsIn UIKit, you import any ttf font files into your projectand create a reference in the info.plist file.In Flutter, place the font file in a folderand reference it in the pubspec.yaml file,similar to how you import images.Then assign the font to your Text widget:<code_start>@overrideWidget build(BuildContext context) {  return Scaffold(    appBar: AppBar(      title: const Text('Sample App'),    ),    body: const Center(      child: Text(        'This is a custom font text',        style: TextStyle(fontFamily: 'MyCustomFont'),      ),    ),  );}<code_end><topic_end><topic_start>Styling textAlong with fonts, you can customize other styling elements on a Text widget.The style parameter of a Text widget takes a TextStyle object,where you can customize many parameters, such as:<topic_end><topic_start>Bundling images in appsWhile iOS treats images and assets as distinct items,Flutter apps have only assets. Resources that areplaced in the Images.xcasset folder on iOS,are placed in an assets’ folder for Flutter.As with iOS, assets are any type of file, not just images.For example, you might have a JSON file located in the my-assets folder:Declare the asset in the pubspec.yaml file:And then access it from code using an AssetBundle:<code_start>import 'dart:async' show Future;import 'package:flutter/services.dart' show rootBundle;Future<String> loadAsset() async {  return await rootBundle.loadString('my-assets/data.json');}<code_end>For images, Flutter follows a simple density-based format like iOS.Image assets might be 1.0x, 2.0x, 3.0x, or any other multiplier.Flutter’s devicePixelRatio expresses the ratioof physical pixels in a single logical pixel.Assets are located in any arbitrary folder—Flutter has no predefined folder structure.You declare the assets (with location) inthe pubspec.yaml file, and Flutter picks them up.For example, to add an image called my_icon.png to your Flutterproject, you might decide to store it in a folder arbitrarily called images.Place the base image (1.0x) in the images folder, and theother variants in sub-folders named after the appropriate ratio multiplier:Next, declare these images in the pubspec.yaml file:You can now access your images using AssetImage:<code_start>AssetImage('images/a_dot_burr.jpeg')<code_end>or directly in an Image widget:<code_start>@overrideWidget build(BuildContext context) {  return Image.asset('images/my_image.png');}<code_end>For more details, seeAdding Assets and Images in Flutter.<topic_end><topic_start>Form inputThis section discusses how to use forms in Flutterand how they compare with UIKit.<topic_end><topic_start>Retrieving user inputGiven how Flutter uses immutable widgets with a separate state,you might be wondering how user input fits into the picture.In UIKit, you usually query the widgets for their current valueswhen it’s time to submit the user input, or action on it.How does that work in Flutter?In practice forms are handled, like everything in Flutter,by specialized widgets. If you have a TextField or aTextFormField, you can supply a TextEditingControllerto retrieve user input:<code_start>class _MyFormState extends State<MyForm> {  // Create a text controller and use it to retrieve the current value.  // of the TextField!  final myController = TextEditingController();  @override  void dispose() {    // Clean up the controller when disposing of the Widget.    myController.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Retrieve Text Input')),      body: Padding(        padding: const EdgeInsets.all(16),        child: TextField(controller: myController),      ),      floatingActionButton: FloatingActionButton(        // When the user presses the button, show an alert dialog with the        // text the user has typed into our text field.        onPressed: () {          showDialog(            context: context,            builder: (context) {              return AlertDialog(                // Retrieve the text the user has typed in using our                // TextEditingController.                content: Text(myController.text),              );            },          );        },        tooltip: 'Show me the value!',        child: const Icon(Icons.text_fields),      ),    );  }}<code_end>You can find more information and the full code listing inRetrieve the value of a text field,from the Flutter cookbook.<topic_end><topic_start>Placeholder in a text fieldIn Flutter, you can easily show a “hint” or a placeholder textfor your field by adding an InputDecoration objectto the decoration constructor parameter for the Text widget:<code_start>Center(  child: TextField(    decoration: InputDecoration(hintText: 'This is a hint'),  ),)<code_end><topic_end><topic_start>Showing validation errorsJust as you would with a “hint”, pass an InputDecoration objectto the decoration constructor for the Text widget.However, you don’t want to start off by showing an error.Instead, when the user has entered invalid data,update the state, and pass a new InputDecoration object.<code_start>import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  // This widget is the root of your application.  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  String? _errorText;  bool isEmail(String em) {    String emailRegexp =        r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|'        r'(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|'        r'(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';    RegExp regExp = RegExp(emailRegexp);    return regExp.hasMatch(em);  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: Center(        child: TextField(          onSubmitted: (text) {            setState(() {              if (!isEmail(text)) {                _errorText = 'Error: This is not an email';              } else {                _errorText = null;              }            });          },          decoration: InputDecoration(            hintText: 'This is a hint',            errorText: _errorText,          ),        ),      ),    );  }}<code_end><topic_end><topic_start>Threading & asynchronicityThis section discusses concurrency in Flutter andhow it compares with UIKit.<topic_end><topic_start>Writing asynchronous codeDart has a single-threaded execution model,with support for Isolates(a way to run Dart code on another thread),an event loop, and asynchronous programming.Unless you spawn an Isolate,your Dart code runs in the main UI thread and isdriven by an event loop. Flutter’s event loop isequivalent to the iOS main loop—that is,the Looper that is attached to the main thread.Dart’s single-threaded model doesn’t mean you arerequired to run everything as a blocking operationthat causes the UI to freeze. Instead,use the asynchronous facilities that the Dart language provides,such as async/await, to perform asynchronous work.For example, you can run network code without causing theUI to hang by using async/await and letting Dart dothe heavy lifting:<code_start>Future<void> loadData() async {  final Uri dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');  final http.Response response = await http.get(dataURL);  setState(() {    data = jsonDecode(response.body);  });}<code_end>Once the awaited network call is done,update the UI by calling setState(),which triggers a rebuild of the widget subtreeand updates the data.The following example loads data asynchronously anddisplays it in a ListView:<code_start>import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List<Map<String, dynamic>> data = <Map<String, dynamic>>[];  @override  void initState() {    super.initState();    loadData();  }  Future<void> loadData() async {    final Uri dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');    final http.Response response = await http.get(dataURL);    setState(() {      data = jsonDecode(response.body);    });  }  Widget getRow(int index) {    return Padding(      padding: const EdgeInsets.all(10),      child: Text('Row ${data[index]['title']}'),    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: ListView.builder(        itemCount: data.length,        itemBuilder: (context, index) {          return getRow(index);        },      ),    );  }}<code_end>Refer to the next section for more information on doing workin the background, and how Flutter differs from iOS.<topic_end><topic_start>Moving to the background threadSince Flutter is single threaded and runs an event loop(like Node.js), you don’t have to worry aboutthread management or spawning background threads.If you’re doing I/O-bound work,such as disk access or a network call,then you can safely use async/await and you’re done.If, on the other hand, you need to do computationally intensivework that keeps the CPU busy, you want to move it to anIsolate to avoid blocking the event loop.For I/O-bound work, declare the function as an async function,and await on long-running tasks inside the function:<code_start>Future<void> loadData() async {  final Uri dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');  final http.Response response = await http.get(dataURL);  setState(() {    data = jsonDecode(response.body);  });}<code_end>This is how you typically do network or database calls,which are both I/O operations.However, there are times when you might be processinga large amount of data and your UI hangs.In Flutter, use Isolates to take advantage ofmultiple CPU cores to do long-running orcomputationally intensive tasks.Isolates are separate execution threads that do not shareany memory with the main execution memory heap.This means you can’t access variables from the main thread,or update your UI by calling setState().Isolates are true to their name, and cannot share memory(in the form of static fields, for example).The following example shows, in a simple isolate,how to share data back to the main thread to update the UI.<code_start>Future<void> loadData() async {  final ReceivePort receivePort = ReceivePort();  await Isolate.spawn(dataLoader, receivePort.sendPort);  // The 'echo' isolate sends its SendPort as the first message.  final SendPort sendPort = await receivePort.first as SendPort;  final List<Map<String, dynamic>> msg = await sendReceive(    sendPort,    'https://jsonplaceholder.typicode.com/posts',  );  setState(() {    data = msg;  });}// The entry point for the isolate.static Future<void> dataLoader(SendPort sendPort) async {  // Open the ReceivePort for incoming messages.  final ReceivePort port = ReceivePort();  // Notify any other isolates what port this isolate listens to.  sendPort.send(port.sendPort);  await for (final dynamic msg in port) {    final String url = msg[0] as String;    final SendPort replyTo = msg[1] as SendPort;    final Uri dataURL = Uri.parse(url);    final http.Response response = await http.get(dataURL);    // Lots of JSON to parse    replyTo.send(jsonDecode(response.body) as List<Map<String, dynamic>>);  }}Future<List<Map<String, dynamic>>> sendReceive(SendPort port, String msg) {  final ReceivePort response = ReceivePort();  port.send(<dynamic>[msg, response.sendPort]);  return response.first as Future<List<Map<String, dynamic>>>;}<code_end>Here, dataLoader() is the Isolate that runs inits own separate execution thread.In the isolate, you can perform more CPU intensiveprocessing (parsing a big JSON, for example),or perform computationally intensive math,such as encryption or signal processing.You can run the full example below:<code_start>import 'dart:async';import 'dart:convert';import 'dart:isolate';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List<Map<String, dynamic>> data = <Map<String, dynamic>>[];  @override  void initState() {    super.initState();    loadData();  }  bool get showLoadingDialog => data.isEmpty;  Future<void> loadData() async {    final ReceivePort receivePort = ReceivePort();    await Isolate.spawn(dataLoader, receivePort.sendPort);    // The 'echo' isolate sends its SendPort as the first message.    final SendPort sendPort = await receivePort.first as SendPort;    final List<Map<String, dynamic>> msg = await sendReceive(      sendPort,      'https://jsonplaceholder.typicode.com/posts',    );    setState(() {      data = msg;    });  }  // The entry point for the isolate.  static Future<void> dataLoader(SendPort sendPort) async {    // Open the ReceivePort for incoming messages.    final ReceivePort port = ReceivePort();    // Notify any other isolates what port this isolate listens to.    sendPort.send(port.sendPort);    await for (final dynamic msg in port) {      final String url = msg[0] as String;      final SendPort replyTo = msg[1] as SendPort;      final Uri dataURL = Uri.parse(url);      final http.Response response = await http.get(dataURL);      // Lots of JSON to parse      replyTo.send(jsonDecode(response.body) as List<Map<String, dynamic>>);    }  }  Future<List<Map<String, dynamic>>> sendReceive(SendPort port, String msg) {    final ReceivePort response = ReceivePort();    port.send(<dynamic>[msg, response.sendPort]);    return response.first as Future<List<Map<String, dynamic>>>;  }  Widget getBody() {    bool showLoadingDialog = data.isEmpty;    if (showLoadingDialog) {      return getProgressDialog();    } else {      return getListView();    }  }  Widget getProgressDialog() {    return const Center(child: CircularProgressIndicator());  }  ListView getListView() {    return ListView.builder(      itemCount: data.length,      itemBuilder: (context, position) {        return getRow(position);      },    );  }  Widget getRow(int i) {    return Padding(      padding: const EdgeInsets.all(10),      child: Text("Row ${data[i]["title"]}"),    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: getBody(),    );  }}<code_end><topic_end><topic_start>Making network requestsMaking a network call in Flutter is easy when youuse the popular http package. This abstractsaway a lot of the networking that you might normallyimplement yourself, making it simple to make network calls.To add the http package as a dependency, run flutter pub add:To make a network call,call await on the async function http.get():<code_start>Future<void> loadData() async {  final Uri dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');  final http.Response response = await http.get(dataURL);  setState(() {    data = jsonDecode(response.body);  });}<code_end><topic_end><topic_start>Showing the progress on long-running tasksIn UIKit, you typically use a UIProgressViewwhile executing a long-running task in the background.In Flutter, use a ProgressIndicator widget.Show the progress programmatically by controllingwhen it’s rendered through a boolean flag.Tell Flutter to update its state before your long-running task starts,and hide it after it ends.In the example below, the build function is separated into three differentfunctions. If showLoadingDialog is true(when widgets.length == 0), then render the ProgressIndicator.Otherwise, render the ListView with the data returned from a network call.<code_start>import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List<Map<String, dynamic>> data = <Map<String, dynamic>>[];  @override  void initState() {    super.initState();    loadData();  }  bool get showLoadingDialog => data.isEmpty;  Future<void> loadData() async {    final Uri dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');    final http.Response response = await http.get(dataURL);    setState(() {      data = jsonDecode(response.body);    });  }  Widget getBody() {    if (showLoadingDialog) {      return getProgressDialog();    }    return getListView();  }  Widget getProgressDialog() {    return const Center(child: CircularProgressIndicator());  }  ListView getListView() {    return ListView.builder(      itemCount: data.length,      itemBuilder: (context, index) {        return getRow(index);      },    );  }  Widget getRow(int i) {    return Padding(      padding: const EdgeInsets.all(10),      child: Text("Row ${data[i]["title"]}"),    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Sample App'),      ),      body: getBody(),    );  }}<code_end><topic_end><topic_start>Flutter for React Native developersThis document is for React Native (RN) developers looking to apply theirexisting RN knowledge to build mobile apps with Flutter. If you understandthe fundamentals of the RN framework then you can use this document as away to get started learning Flutter development.This document can be used as a cookbook by jumping around and findingquestions that are most relevant to your needs.<topic_end><topic_start>Introduction to Dart for JavaScript Developers (ES6)Like React Native, Flutter uses reactive-style views. However, while RNtranspiles to native widgets, Flutter compiles all the way to native code.Flutter controls each pixel on the screen, which avoids performance problemscaused by the need for a JavaScript bridge.Dart is an easy language to learn and offers the following features:A few examples of the differences between JavaScript and Dart are describedbelow.<topic_end><topic_start>Entry pointJavaScript doesn’t have a pre-defined entryfunction—you define the entry point.In Dart, every app must have a top-level main() function that serves as theentry point to the app.<code_start>/// Dartvoid main() {}<code_end>Try it out in DartPad.<topic_end><topic_start>Printing to the consoleTo print to the console in Dart, use print().<code_start>/// Dartprint('Hello world!');<code_end>Try it out in DartPad.<topic_end><topic_start>VariablesDart is type safe—it uses a combination of static type checkingand runtime checks to ensure that a variable’s value always matchesthe variable’s static type. Although types are mandatory,some type annotations are optional becauseDart performs type inference.<topic_end><topic_start>Creating and assigning variablesIn JavaScript, variables cannot be typed.In Dart, variables must either be explicitlytyped or the type system must infer the proper type automatically.<code_start>/// Dart/// Both variables are acceptable.String name = 'dart'; // Explicitly typed as a [String].var otherName = 'Dart'; // Inferred [String] type.<code_end>Try it out in DartPad.For more information, see Dart’s Type System.<topic_end><topic_start>Default valueIn JavaScript, uninitialized variables are undefined.In Dart, uninitialized variables have an initial value of null.Because numbers are objects in Dart, even uninitialized variables withnumeric types have the value null.info Note  As of 2.12, Dart supports Sound Null Safety,  all underlying types are non-nullable by default,  which must be initialized as a non-nullable value.<code_start>// Dartvar name; // == null; raises a linter warningint? x; // == null<code_end>Try it out in DartPad.For more information, see the documentation onvariables.<topic_end><topic_start>Checking for null or zeroIn JavaScript, values of 1 or any non-null objectsare treated as true when using the == comparison operator.In Dart, only the boolean value true is treated as true.<code_start>/// Dartvar myNull;var zero = 0;if (zero == 0) {  print('use "== 0" to check zero');}<code_end>Try it out in DartPad.<topic_end><topic_start>FunctionsDart and JavaScript functions are generally similar.The primary difference is the declaration.<code_start>/// Dart/// You can explicitly define the return type.bool fn() {  return true;}<code_end>Try it out in DartPad.For more information, see the documentation onfunctions.<topic_end><topic_start>Asynchronous programming<topic_end><topic_start>FuturesLike JavaScript, Dart supports single-threaded execution. In JavaScript,the Promise object represents the eventual completion (or failure)of an asynchronous operation and its resulting value.Dart uses Future objects to handle this.<code_start>// Dartimport 'dart:convert';import 'package:http/http.dart' as http;class Example {  Future<String> _getIPAddress() {    final url = Uri.https('httpbin.org', '/ip');    return http.get(url).then((response) {      final ip = jsonDecode(response.body)['origin'] as String;      return ip;    });  }}void main() {  final example = Example();  example      ._getIPAddress()      .then((ip) => print(ip))      .catchError((error) => print(error));}<code_end>For more information, see the documentation onFuture objects.<topic_end><topic_start>async and awaitThe async function declaration defines an asynchronous function.In JavaScript, the async function returns a Promise.The await operator is used to wait for a Promise.In Dart, an async function returns a Future,and the body of the function is scheduled for execution later.The await operator is used to wait for a Future.<code_start>// Dartimport 'dart:convert';import 'package:http/http.dart' as http;class Example {  Future<String> _getIPAddress() async {    final url = Uri.https('httpbin.org', '/ip');    final response = await http.get(url);    final ip = jsonDecode(response.body)['origin'] as String;    return ip;  }}/// An async function returns a `Future`./// It can also return `void`, unless you use/// the `avoid_void_async` lint. In that case,/// return `Future<void>`.void main() async {  final example = Example();  try {    final ip = await example._getIPAddress();    print(ip);  } catch (error) {    print(error);  }}<code_end>For more information, see the documentation for async and await.<topic_end><topic_start>The basics<topic_end><topic_start>How do I create a Flutter app?To create an app using React Native,you would run create-react-native-app from the command line.To create an app in Flutter, do one of the following:For more information, see Getting started, whichwalks you through creating a button-click counter app.Creating a Flutter project builds all the files that youneed to run a sample app on both Android and iOS devices.<topic_end><topic_start>How do I run my app?In React Native, you would run npm run or yarn run from the projectdirectory.You can run Flutter apps in a couple of ways:Your app runs on a connected device, the iOS simulator,or the Android emulator.For more information, see the Flutter Getting starteddocumentation.<topic_end><topic_start>How do I import widgets?In React Native, you need to import each required component.In Flutter, to use widgets from the Material Design library,import the material.dart package. To use iOS style widgets,import the Cupertino library. To use a more basic widget set,import the Widgets library.Or, you can write your own widget library and import that.<code_start>import 'package:flutter/cupertino.dart';import 'package:flutter/material.dart';import 'package:flutter/widgets.dart';import 'package:my_widgets/my_widgets.dart';<code_end>Whichever widget package you import,Dart pulls in only the widgets that are used in your app.For more information, see the Flutter Widget Catalog.<topic_end><topic_start>What is the equivalent of the React Native “Hello world!” app in Flutter?In React Native, the HelloWorldApp class extends React.Component andimplements the render method by returning a view component.In Flutter, you can create an identical “Hello world!” app using theCenter and Text widgets from the core widget library.The Center widget becomes the root of the widget tree and has one child,the Text widget.<code_start>// Flutterimport 'package:flutter/material.dart';void main() {  runApp(    const Center(      child: Text(        'Hello, world!',        textDirection: TextDirection.ltr,      ),    ),  );}<code_end>The following images show the Android and iOS UI for the basic Flutter“Hello world!” app.Now that you’ve seen the most basic Flutter app, the next section shows how totake advantage of Flutter’s rich widget libraries to create a modern, polishedapp.<topic_end><topic_start>How do I use widgets and nest them to form a widget tree?In Flutter, almost everything is a widget.Widgets are the basic building blocks of an app’s user interface.You compose widgets into a hierarchy, called a widget tree.Each widget nests inside a parent widgetand inherits properties from its parent.Even the application object itself is a widget.There is no separate “application” object.Instead, the root widget serves this role.A widget can define:The following example shows the “Hello world!” app using widgets from theMaterial library. In this example, the widget tree is nested inside theMaterialApp root widget.<code_start>// Flutterimport 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Welcome to Flutter',      home: Scaffold(        appBar: AppBar(          title: const Text('Welcome to Flutter'),        ),        body: const Center(          child: Text('Hello world'),        ),      ),    );  }}<code_end>The following images show “Hello world!” built from Material Design widgets.You get more functionality for free than in the basic “Hello world!” app.When writing an app, you’ll use two types of widgets:StatelessWidget or StatefulWidget.A StatelessWidget is just what it sounds like—awidget with no state. A StatelessWidget is created once,and never changes its appearance.A StatefulWidget dynamically changes state based on datareceived, or user input.The important difference between stateless and statefulwidgets is that StatefulWidgets have a State objectthat stores state data and carries it overacross tree rebuilds, so it’s not lost.In simple or basic apps it’s easy to nest widgets,but as the code base gets larger and the app becomes complex,you should break deeply nested widgets intofunctions that return the widget or smaller classes.Creating separate functionsand widgets allows you to reuse the components within the app.<topic_end><topic_start>How do I create reusable components?In React Native, you would define a class to create areusable component and then use props methods to setor return properties and values of the selected elements.In the example below, the CustomCard class is definedand then used inside a parent class.In Flutter, define a class to create a custom widget and then reuse thewidget. You can also define and call a function that returns areusable widget as shown in the build function in the following example.<code_start>/// Flutterclass CustomCard extends StatelessWidget {  const CustomCard({    super.key,    required this.index,    required this.onPress,  });  final int index;  final void Function() onPress;  @override  Widget build(BuildContext context) {    return Card(      child: Column(        children: <Widget>[          Text('Card $index'),          TextButton(            onPressed: onPress,            child: const Text('Press'),          ),        ],      ),    );  }}class UseCard extends StatelessWidget {  const UseCard({super.key, required this.index});  final int index;  @override  Widget build(BuildContext context) {    /// Usage    return CustomCard(      index: index,      onPress: () {        print('Card $index');      },    );  }}<code_end>In the previous example, the constructor for the CustomCardclass uses Dart’s curly brace syntax { } to indicate named parameters.To require these fields, either remove the curly braces fromthe constructor, or add required to the constructor.The following screenshots show an example of the reusableCustomCard class.<topic_end><topic_start>Project structure and resources<topic_end><topic_start>Where do I start writing the code?Start with the lib/main.dart file.It’s autogenerated when you create a Flutter app.<code_start>// Dartvoid main() {  print('Hello, this is the main function.');}<code_end>In Flutter, the entry point file is{project_name}/lib/main.dart and executionstarts from the main function.<topic_end><topic_start>How are files structured in a Flutter app?When you create a new Flutter project,it builds the following directory structure.You can customize it later, but this is where you start.<topic_end><topic_start>Where do I put my resources and assets and how do I use them?A Flutter resource or asset is a file that is bundled and deployedwith your app and is accessible at runtime.Flutter apps can include the following asset types:Flutter uses the pubspec.yaml file,located at the root of your project, toidentify assets required by an app.The assets subsection specifies files that should be included with the app.Each asset is identified by an explicit pathrelative to the pubspec.yaml file, where the asset file is located.The order in which the assets are declared does not matter.The actual directory used (assets in this case) does not matter.However, while assets can be placed in any app directory, it’s abest practice to place them in the assets directory.During a build, Flutter places assets into a special archivecalled the asset bundle, which apps read from at runtime.When an asset’s path is specified in the assets’ section of pubspec.yaml,the build process looks for any fileswith the same name in adjacent subdirectories.These files are also included in the asset bundlealong with the specified asset. Flutter uses asset variantswhen choosing resolution-appropriate images for your app.In React Native, you would add a static image by placing the image filein a source code directory and referencing it.In Flutter, add a static image to your appusing the Image.asset constructor in a widget’s build method.<code_start>Image.asset('assets/background.png');<code_end>For more information, see Adding Assets and Images in Flutter.<topic_end><topic_start>How do I load images over a network?In React Native, you would specify the uri in thesource prop of the Image component and also provide thesize if needed.In Flutter, use the Image.network constructor to includean image from a URL.<code_start>Image.network('https://docs.flutter.dev/assets/images/docs/owl.jpg');<code_end><topic_end><topic_start>How do I install packages and package plugins?Flutter supports using shared packages contributed by other developers to theFlutter and Dart ecosystems. This allows you to quickly build your app withouthaving to develop everything from scratch. Packages that containplatform-specific code are known as package plugins.In React Native, you would use yarn add {package-name} ornpm install --save {package-name} to install packagesfrom the command line.In Flutter, install a package using the following instructions:<code_start>import 'package:flutter/material.dart';<code_end>For more information, see Using Packages andDeveloping Packages & Plugins.You can find many packages shared by Flutter developers in theFlutter packages section of pub.dev.<topic_end><topic_start>Flutter widgetsIn Flutter, you build your UI out of widgets that describe what their viewshould look like given their current configuration and state.Widgets are often composed of many small,single-purpose widgets that are nested to produce powerful effects.For example, the Container widget consists ofseveral widgets responsible for layout, painting, positioning, and sizing.Specifically, the Container widget includes the LimitedBox,ConstrainedBox, Align, Padding, DecoratedBox, and Transform widgets.Rather than subclassing Container to produce a customized effect, you cancompose these and other simple widgets in new and unique ways.The Center widget is another example of how you can control the layout.To center a widget, wrap it in a Center widget and then use layoutwidgets for alignment, row, columns, and grids.These layout widgets do not have a visual representation of their own.Instead, their sole purpose is to control some aspect of anotherwidget’s layout. To understand why a widget renders in acertain way, it’s often helpful to inspect the neighboring widgets.For more information, see the Flutter Technical Overview.For more information about the core widgets from the Widgets package,see Flutter Basic Widgets,the Flutter Widget Catalog,or the Flutter Widget Index.<topic_end><topic_start>Views<topic_end><topic_start>What is the equivalent of the View container?In React Native, View is a container that supports layout with Flexbox,style, touch handling, and accessibility controls.In Flutter, you can use the core layout widgets in the Widgetslibrary, such as Container, Column,Row, and Center.For more information, see the Layout Widgets catalog.<topic_end><topic_start>What is the equivalent of FlatList or SectionList?A List is a scrollable list of components arranged vertically.In React Native, FlatList or SectionList are used to render simple orsectioned lists.ListView is Flutter’s most commonly used scrolling widget.The default constructor takes an explicit list of children.ListView is most appropriate for a small number of widgets.For a large or infinite list, use ListView.builder,which builds its children on demand and only buildsthose children that are visible.<code_start>var data = [  'Hello',  'World',];return ListView.builder(  itemCount: data.length,  itemBuilder: (context, index) {    return Text(data[index]);  },);<code_end>To learn how to implement an infinite scrolling list, see the officialinfinite_list sample.<topic_end><topic_start>How do I use a Canvas to draw or paint?In React Native, canvas components aren’t presentso third party libraries like react-native-canvas are used.In Flutter, you can use the CustomPaintand CustomPainter classes to draw to the canvas.The following example shows how to draw during the paint phase using theCustomPaint widget. It implements the abstract class, CustomPainter,and passes it to CustomPaint’s painter property.CustomPaint subclasses must implement the paint()and shouldRepaint() methods.<code_start>class MyCanvasPainter extends CustomPainter {  const MyCanvasPainter();  @override  void paint(Canvas canvas, Size size) {    final Paint paint = Paint()..color = Colors.amber;    canvas.drawCircle(const Offset(100, 200), 40, paint);    final Paint paintRect = Paint()..color = Colors.lightBlue;    final Rect rect = Rect.fromPoints(      const Offset(150, 300),      const Offset(300, 400),    );    canvas.drawRect(rect, paintRect);  }  @override  bool shouldRepaint(MyCanvasPainter oldDelegate) => false;}class MyCanvasWidget extends StatelessWidget {  const MyCanvasWidget({super.key});  @override  Widget build(BuildContext context) {    return const Scaffold(      body: CustomPaint(painter: MyCanvasPainter()),    );  }}<code_end><topic_end><topic_start>Layouts<topic_end><topic_start>How do I use widgets to define layout properties?In React Native, most of the layout can be done with the propsthat are passed to a specific component.For example, you could use the style prop on the View componentin order to specify the flexbox properties.To arrange your components in a column, you would specify a prop such as:flexDirection: 'column'.In Flutter, the layout is primarily defined by widgetsspecifically designed to provide layout,combined with control widgets and their style properties.For example, the Column and Row widgetstake an array of children and align themvertically and horizontally respectively.A Container widget takes a combination oflayout and styling properties, and aCenter widget centers its child widgets.<code_start>@overrideWidget build(BuildContext context) {  return Center(    child: Column(      children: <Widget>[        Container(          color: Colors.red,          width: 100,          height: 100,        ),        Container(          color: Colors.blue,          width: 100,          height: 100,        ),        Container(          color: Colors.green,          width: 100,          height: 100,        ),      ],    ),  );<code_end>Flutter provides a variety of layout widgets in its core widget library.For example, Padding, Align, and Stack.For a complete list, see Layout Widgets.<topic_end><topic_start>How do I layer widgets?In React Native, components can be layered using absolute positioning.Flutter uses the Stackwidget to arrange children widgets in layers.The widgets can entirely or partially overlap the base widget.The Stack widget positions its children relative to the edges of its box.This class is useful if you simply want to overlap several children widgets.<code_start>@overrideWidget build(BuildContext context) {  return Stack(    alignment: const Alignment(0.6, 0.6),    children: <Widget>[      const CircleAvatar(        backgroundImage: NetworkImage(          'https://avatars3.githubusercontent.com/u/14101776?v=4',        ),      ),      Container(        color: Colors.black45,        child: const Text('Flutter'),      ),    ],  );<code_end>The previous example uses Stack to overlay a Container(that displays its Text on a translucent black background)on top of a CircleAvatar.The Stack offsets the text using the alignment propertyand Alignment coordinates.For more information, see the Stack class documentation.<topic_end><topic_start>Styling<topic_end><topic_start>How do I style my components?In React Native, inline styling and stylesheets.createare used to style components.In Flutter, a Text widget can take a TextStyle classfor its style property. If you want to use the same textstyle in multiple places, you can create aTextStyle class and use it for multiple Text widgets.<code_start>const TextStyle textStyle = TextStyle(  color: Colors.cyan,  fontSize: 32,  fontWeight: FontWeight.w600,);return const Center(  child: Column(    children: <Widget>[      Text('Sample text', style: textStyle),      Padding(        padding: EdgeInsets.all(20),        child: Icon(          Icons.lightbulb_outline,          size: 48,          color: Colors.redAccent,        ),      ),    ],  ),);<code_end><topic_end><topic_start>How do I use Icons and Colors?React Native doesn’t include support for iconsso third party libraries are used.In Flutter, importing the Material library also pulls in therich set of Material icons and colors.<code_start>return const Icon(Icons.lightbulb_outline, color: Colors.redAccent);<code_end>When using the Icons class,make sure to set uses-material-design: true inthe project’s pubspec.yaml file.This ensures that the MaterialIcons font,which displays the icons, is included in your app.In general, if you intend to use the Material library,you should include this line.Flutter’s Cupertino (iOS-style) package provides highfidelity widgets for the current iOS design language.To use the CupertinoIcons font,add a dependency for cupertino_icons in your project’s pubspec.yaml file.To globally customize the colors and styles of components,use ThemeData to specify default colorsfor various aspects of the theme.Set the theme property in MaterialApp to the ThemeData object.The Colors class provides colorsfrom the Material Design color palette.The following example sets the color scheme from seed to deepPurpleand the text selection to red.<code_start>class SampleApp extends StatelessWidget {  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Sample App',      theme: ThemeData(          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),          textSelectionTheme:              const TextSelectionThemeData(selectionColor: Colors.red)),      home: const SampleAppPage(),    );  }}<code_end><topic_end><topic_start>How do I add style themes?In React Native, common themes are defined forcomponents in stylesheets and then used in components.In Flutter, create uniform styling for almost everythingby defining the styling in the ThemeDataclass and passing it to the theme property in theMaterialApp widget.<code_start>@overrideWidget build(BuildContext context) {  return MaterialApp(    theme: ThemeData(      primaryColor: Colors.cyan,      brightness: Brightness.dark,    ),    home: const StylingPage(),  );}<code_end>A Theme can be applied even without using the MaterialApp widget.The Theme widget takes a ThemeData in its data parameterand applies the ThemeData to all of its children widgets.<code_start>@overrideWidget build(BuildContext context) {  return Theme(    data: ThemeData(      primaryColor: Colors.cyan,      brightness: brightness,    ),    child: Scaffold(      backgroundColor: Theme.of(context).primaryColor,      //...    ),  );}<code_end><topic_end><topic_start>State managementState is information that can be read synchronouslywhen a widget is built or informationthat might change during the lifetime of a widget.To manage app state in Flutter,use a StatefulWidget paired with a State object.For more information on ways to approach managing state in Flutter,see State management.<topic_end><topic_start>The StatelessWidgetA StatelessWidget in Flutter is a widgetthat doesn’t require a state change—it has no internal state to manage.Stateless widgets are useful when the part of the user interfaceyou are describing does not depend on anything other than theconfiguration information in the object itself and theBuildContext in which the widget is inflated.AboutDialog, CircleAvatar, and Text are examplesof stateless widgets that subclass StatelessWidget.<code_start>import 'package:flutter/material.dart';void main() => runApp(      const MyStatelessWidget(        text: 'StatelessWidget Example to show immutable data',      ),    );class MyStatelessWidget extends StatelessWidget {  const MyStatelessWidget({    super.key,    required this.text,  });  final String text;  @override  Widget build(BuildContext context) {    return Center(      child: Text(        text,        textDirection: TextDirection.ltr,      ),    );  }}<code_end>The previous example uses the constructor of the MyStatelessWidgetclass to pass the text, which is marked as final.This class extends StatelessWidget—it contains immutable data.The build method of a stateless widget is typically calledin only three situations:<topic_end><topic_start>The StatefulWidgetA StatefulWidget is a widget that changes state.Use the setState method to manage thestate changes for a StatefulWidget.A call to setState() tells the Flutterframework that something has changed in a state,which causes an app to rerun the build() methodso that the app can reflect the change.State is information that can be read synchronously when a widgetis built and might change during the lifetime of the widget.It’s the responsibility of the widget implementer to ensure thatthe state object is promptly notified when the state changes.Use StatefulWidget when a widget can change dynamically.For example, the state of the widget changes by typing into a form,or moving a slider.Or, it can change over time—perhaps a data feed updates the UI.Checkbox, Radio, Slider, InkWell,Form, and TextFieldare examples of stateful widgets that subclassStatefulWidget.The following example declares a StatefulWidgetthat requires a createState() method.This method creates the state object that manages the widget’s state,_MyStatefulWidgetState.<code_start>class MyStatefulWidget extends StatefulWidget {  const MyStatefulWidget({    super.key,    required this.title,  });  final String title;  @override  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();}<code_end>The following state class, _MyStatefulWidgetState,implements the build() method for the widget.When the state changes, for example, when the user togglesthe button, setState() is called with the new toggle value.This causes the framework to rebuild this widget in the UI.<code_start>class _MyStatefulWidgetState extends State<MyStatefulWidget> {  bool showText = true;  bool toggleState = true;  Timer? t2;  void toggleBlinkState() {    setState(() {      toggleState = !toggleState;    });    if (!toggleState) {      t2 = Timer.periodic(const Duration(milliseconds: 1000), (t) {        toggleShowText();      });    } else {      t2?.cancel();    }  }  void toggleShowText() {    setState(() {      showText = !showText;    });  }  @override  Widget build(BuildContext context) {    return Scaffold(      body: Center(        child: Column(          children: <Widget>[            if (showText)              const Text(                'This execution will be done before you can blink.',              ),            Padding(              padding: const EdgeInsets.only(top: 70),              child: ElevatedButton(                onPressed: toggleBlinkState,                child: toggleState                    ? const Text('Blink')                    : const Text('Stop Blinking'),              ),            ),          ],        ),      ),    );  }}<code_end><topic_end><topic_start>What are the StatefulWidget and StatelessWidget best practices?Here are a few things to consider when designing your widget.In Flutter, widgets are either Stateful or Stateless—depending on whetherthey depend on a state change.In Flutter, there are three primary ways to manage state:When deciding which approach to use, consider the following principles:The MyStatefulWidget class manages its own state—it extendsStatefulWidget, it overrides the createState()method to create the State object,and the framework calls createState() to build the widget.In this example, createState() creates an instance of_MyStatefulWidgetState, whichis implemented in the next best practice.<code_start>class MyStatefulWidget extends StatefulWidget {  const MyStatefulWidget({    super.key,    required this.title,  });  final String title;  @override  State<MyStatefulWidget> createState() => _MyStatefulWidgetState();}class _MyStatefulWidgetState extends State<MyStatefulWidget> {  @override  Widget build(BuildContext context) {    //...  }}<code_end>Add your custom StatefulWidget to the widget treein the app’s build method.<code_start>class MyStatelessWidget extends StatelessWidget {  // This widget is the root of your application.  const MyStatelessWidget({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Flutter Demo',      home: MyStatefulWidget(title: 'State Change Demo'),    );  }}<code_end><topic_end><topic_start>PropsIn React Native, most components can be customized when they arecreated with different parameters or properties, called props.These parameters can be used in a child component using this.props.In Flutter, you assign a local variable or function markedfinal with the property received in the parameterized constructor.<code_start>/// Flutterclass CustomCard extends StatelessWidget {  const CustomCard({    super.key,    required this.index,    required this.onPress,  });  final int index;  final void Function() onPress;  @override  Widget build(BuildContext context) {    return Card(      child: Column(        children: <Widget>[          Text('Card $index'),          TextButton(            onPressed: onPress,            child: const Text('Press'),          ),        ],      ),    );  }}class UseCard extends StatelessWidget {  const UseCard({super.key, required this.index});  final int index;  @override  Widget build(BuildContext context) {    /// Usage    return CustomCard(      index: index,      onPress: () {        print('Card $index');      },    );  }}<code_end><topic_end><topic_start>Local storageIf you don’t need to store a lot of data, and it doesn’t requirestructure, you can use shared_preferences which allows you toread and write persistent key-value pairs of primitive datatypes: booleans, floats, ints, longs, and strings.<topic_end><topic_start>How do I store persistent key-value pairs that are global to the app?In React Native, you use the setItem and getItem functionsof the AsyncStorage component to store and retrieve datathat is persistent and global to the app.In Flutter, use the shared_preferences plugin tostore and retrieve key-value data that is persistent and globalto the app. The shared_preferences plugin wrapsNSUserDefaults on iOS and SharedPreferences on Android,providing a persistent store for simple data.To add the shared_preferences package as a dependency, run flutter pub add:<code_start>import 'package:shared_preferences/shared_preferences.dart';<code_end>To implement persistent data, use the setter methodsprovided by the SharedPreferences class.Setter methods are available for various primitivetypes, such as setInt, setBool, and setString.To read data, use the appropriate getter method providedby the SharedPreferences class. For eachsetter there is a corresponding getter method,for example, getInt, getBool, and getString.<code_start>Future<void> updateCounter() async {  final prefs = await SharedPreferences.getInstance();  int? counter = prefs.getInt('counter');  if (counter is int) {    await prefs.setInt('counter', ++counter);  }  setState(() {    _counter = counter;  });}<code_end><topic_end><topic_start>RoutingMost apps contain several screens for displaying differenttypes of information. For example, you might have a productscreen that displays images where users could tap on a productimage to get more information about the product on a new screen.In Android, new screens are new Activities.In iOS, new screens are new ViewControllers. In Flutter,screens are just Widgets! And to navigate to newscreens in Flutter, use the Navigator widget.<topic_end><topic_start>How do I navigate between screens?In React Native, there are three main navigators:StackNavigator, TabNavigator, and DrawerNavigator.Each provides a way to configure and define the screens.In Flutter, there are two main widgets used to navigate between screens:A Navigator is defined as a widget that manages a set of childwidgets with a stack discipline. The navigator manages a stackof Route objects and provides methods for managing the stack,like Navigator.push and Navigator.pop.A list of routes might be specified in the MaterialApp widget,or they might be built on the fly, for example, in hero animations.The following example specifies named routes in the MaterialApp widget.info Note  Named routes are no longer recommended for most  applications. For more information, see  Limitations in the navigation overview page.<code_start>class NavigationApp extends StatelessWidget {  // This widget is the root of your application.  const NavigationApp({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      //...      routes: <String, WidgetBuilder>{        '/a': (context) => const UsualNavScreen(),        '/b': (context) => const DrawerNavScreen(),      },      //...    );  }}<code_end>To navigate to a named route, the Navigator.of()method is used to specify the BuildContext(a handle to the location of a widget in the widget tree).The name of the route is passed to the pushNamed function tonavigate to the specified route.<code_start>Navigator.of(context).pushNamed('/a');<code_end>You can also use the push method of Navigator whichadds the given Route to the history of thenavigator that most tightly encloses the given BuildContext,and transitions to it. In the following example,the MaterialPageRoute widget is a modal route thatreplaces the entire screen with a platform-adaptivetransition. It takes a WidgetBuilder as a required parameter.<code_start>Navigator.push(  context,  MaterialPageRoute(    builder: (context) => const UsualNavScreen(),  ),);<code_end><topic_end><topic_start>How do I use tab navigation and drawer navigation?In Material Design apps, there are two primary optionsfor Flutter navigation: tabs and drawers.When there is insufficient space to support tabs, drawersprovide a good alternative.<topic_end><topic_start>Tab navigationIn React Native, createBottomTabNavigatorand TabNavigation are used toshow tabs and for tab navigation.Flutter provides several specialized widgets for drawer andtab navigation:<code_start>class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {  late TabController controller = TabController(length: 2, vsync: this);  @override  Widget build(BuildContext context) {    return TabBar(      controller: controller,      tabs: const <Tab>[        Tab(icon: Icon(Icons.person)),        Tab(icon: Icon(Icons.email)),      ],    );  }}<code_end>A TabController is required to coordinate the tab selectionbetween a TabBar and a TabBarView.The TabController constructor length argument is the totalnumber of tabs. A TickerProvider is required to triggerthe notification whenever a frame triggers a state change.The TickerProvider is vsync. Pass thevsync: this argument to the TabController constructorwhenever you create a new TabController.The TickerProvider is an interface implementedby classes that can vend Ticker objects.Tickers can be used by any object that must be notified whenever aframe triggers, but they’re most commonly used indirectly via anAnimationController. AnimationControllersneed a TickerProvider to obtain their Ticker.If you are creating an AnimationController from a State,then you can use the TickerProviderStateMixinor SingleTickerProviderStateMixinclasses to obtain a suitable TickerProvider.The Scaffold widget wraps a new TabBar widget andcreates two tabs. The TabBarView widgetis passed as the body parameter of the Scaffold widget.All screens corresponding to the TabBar widget’s tabs arechildren to the TabBarView widget along with the same TabController.<code_start>class _NavigationHomePageState extends State<NavigationHomePage>    with SingleTickerProviderStateMixin {  late TabController controller = TabController(length: 2, vsync: this);  @override  Widget build(BuildContext context) {    return Scaffold(        bottomNavigationBar: Material(          color: Colors.blue,          child: TabBar(            tabs: const <Tab>[              Tab(                icon: Icon(Icons.person),              ),              Tab(                icon: Icon(Icons.email),              ),            ],            controller: controller,          ),        ),        body: TabBarView(          controller: controller,          children: const <Widget>[HomeScreen(), TabScreen()],        ));  }}<code_end><topic_end><topic_start>Drawer navigationIn React Native, import the needed react-navigation packages and then usecreateDrawerNavigator and DrawerNavigation.In Flutter, we can use the Drawer widget in combination with aScaffold to create a layout with a Material Design drawer.To add a Drawer to an app, wrap it in a Scaffold widget.The Scaffold widget provides a consistentvisual structure to apps that follow theMaterial Design guidelines. It also supportsspecial Material Design components,such as Drawers, AppBars, and SnackBars.The Drawer widget is a Material Design panel that slidesin horizontally from the edge of a Scaffold to show navigationlinks in an application. You canprovide a ElevatedButton, a Text widget,or a list of items to display as the child to the Drawer widget.In the following example, the ListTilewidget provides the navigation on tap.<code_start>@overrideWidget build(BuildContext context) {  return Drawer(    elevation: 20,    child: ListTile(      leading: const Icon(Icons.change_history),      title: const Text('Screen2'),      onTap: () {        Navigator.of(context).pushNamed('/b');      },    ),  );}<code_end>The Scaffold widget also includes an AppBar widget that automaticallydisplays an appropriate IconButton to show the Drawer when a Drawer isavailable in the Scaffold. The Scaffold automatically handles theedge-swipe gesture to show the Drawer.<code_start>@overrideWidget build(BuildContext context) {  return Scaffold(    drawer: Drawer(      elevation: 20,      child: ListTile(        leading: const Icon(Icons.change_history),        title: const Text('Screen2'),        onTap: () {          Navigator.of(context).pushNamed('/b');        },      ),    ),    appBar: AppBar(title: const Text('Home')),    body: Container(),  );}<code_end><topic_end><topic_start>Gesture detection and touch event handlingTo listen for and respond to gestures,Flutter supports taps, drags, and scaling.The gesture system in Flutter has two separate layers.The first layer includes raw pointer events,which describe the location and movement of pointers,(such as touches, mice, and styli movements), across the screen.The second layer includes gestures,which describe semantic actionsthat consist of one or more pointer movements.<topic_end><topic_start>How do I add a click or press listeners to a widget?In React Native, listeners are added to componentsusing PanResponder or the Touchable components.For more complex gestures and combining several touches intoa single gesture, PanResponder is used.In Flutter, to add a click (or press) listener to a widget,use a button or a touchable widget that has an onPress: field.Or, add gesture detection to any widget by wrapping itin a GestureDetector.<code_start>@overrideWidget build(BuildContext context) {  return GestureDetector(    child: Scaffold(      appBar: AppBar(title: const Text('Gestures')),      body: const Center(          child: Column(        mainAxisAlignment: MainAxisAlignment.center,        children: <Widget>[          Text('Tap, Long Press, Swipe Horizontally or Vertically'),        ],      )),    ),    onTap: () {      print('Tapped');    },    onLongPress: () {      print('Long Pressed');    },    onVerticalDragEnd: (value) {      print('Swiped Vertically');    },    onHorizontalDragEnd: (value) {      print('Swiped Horizontally');    },  );}<code_end>For more information, including a list ofFlutter GestureDetector callbacks,see the GestureDetector class.<topic_end><topic_start>Making HTTP network requestsFetching data from the internet is common for most apps. And in Flutter,the http package provides the simplest way to fetch data from the internet.<topic_end><topic_start>How do I fetch data from API calls?React Native provides the Fetch API for networking—you make a fetch requestand then receive the response to get the data.Flutter uses the http package.To add the http package as a dependency, run flutter pub add:Flutter uses the dart:io core HTTP support client.To create an HTTP Client, import dart:io.<code_start>import 'dart:io';<code_end>The client supports the following HTTP operations:GET, POST, PUT, and DELETE.<code_start>final url = Uri.parse('https://httpbin.org/ip');final httpClient = HttpClient();Future<void> getIPAddress() async {  final request = await httpClient.getUrl(url);  final response = await request.close();  final responseBody = await response.transform(utf8.decoder).join();  final ip = jsonDecode(responseBody)['origin'] as String;  setState(() {    _ipAddress = ip;  });}<code_end><topic_end><topic_start>Form inputText fields allow users to type text into your app so they can beused to build forms, messaging apps, search experiences, and more.Flutter provides two core text field widgets:TextField and TextFormField.<topic_end><topic_start>How do I use text field widgets?In React Native, to enter text you use a TextInput component to show a textinput box and then use the callback to store the value in a variable.In Flutter, use the TextEditingControllerclass to manage a TextField widget.Whenever the text field is modified,the controller notifies its listeners.Listeners read the text and selection properties tolearn what the user typed into the field.You can access the text in TextFieldby the text property of the controller.<code_start>final TextEditingController _controller = TextEditingController();@overrideWidget build(BuildContext context) {  return Column(children: [    TextField(      controller: _controller,      decoration: const InputDecoration(        hintText: 'Type something',        labelText: 'Text Field',      ),    ),    ElevatedButton(      child: const Text('Submit'),      onPressed: () {        showDialog(            context: context,            builder: (context) {              return AlertDialog(                title: const Text('Alert'),                content: Text('You typed ${_controller.text}'),              );            });      },    ),  ]);}<code_end>In this example, when a user clicks on the submit button an alert dialogdisplays the current text entered in the text field.This is achieved using an AlertDialogwidget that displays the alert message, and the text fromthe TextField is accessed by the text property of theTextEditingController.<topic_end><topic_start>How do I use Form widgets?In Flutter, use the Form widget whereTextFormField widgets along with the submitbutton are passed as children.The TextFormField widget has a parameter calledonSaved that takes a callback and executeswhen the form is saved. A FormStateobject is used to save, reset, or validateeach FormField that is a descendant of this Form.To obtain the FormState, you can use Form.of()with a context whose ancestor is the Form,or pass a GlobalKey to the Form constructor and callGlobalKey.currentState().<code_start>@overrideWidget build(BuildContext context) {  return Form(    key: formKey,    child: Column(      children: <Widget>[        TextFormField(          validator: (value) {            if (value != null && value.contains('@')) {              return null;            }            return 'Not a valid email.';          },          onSaved: (val) {            _email = val;          },          decoration: const InputDecoration(            hintText: 'Enter your email',            labelText: 'Email',          ),        ),        ElevatedButton(          onPressed: _submit,          child: const Text('Login'),        ),      ],    ),  );}<code_end>The following example shows how Form.save() and formKey(which is a GlobalKey), are used to save the form on submit.<code_start>void _submit() {  final form = formKey.currentState;  if (form != null && form.validate()) {    form.save();    showDialog(      context: context,      builder: (context) {        return AlertDialog(            title: const Text('Alert'),            content: Text('Email: $_email, password: $_password'));      },    );  }}<code_end><topic_end><topic_start>Platform-specific codeWhen building a cross-platform app, you want to re-use as much code aspossible across platforms. However, scenarios might arise where itmakes sense for the code to be different depending on the OS.This requires a separate implementation by declaring a specific platform.In React Native, the following implementation would be used:In Flutter, use the following implementation:<code_start>final platform = Theme.of(context).platform;if (platform == TargetPlatform.iOS) {  return 'iOS';}if (platform == TargetPlatform.android) {  return 'android';}if (platform == TargetPlatform.fuchsia) {  return 'fuchsia';}return 'not recognized ';<code_end><topic_end><topic_start>Debugging<topic_end><topic_start>What tools can I use to debug my app in Flutter?Use the DevTools suite for debugging Flutter or Dart apps.DevTools includes support for profiling, examining the heap,inspecting the widget tree, logging diagnostics, debugging,observing executed lines of code, debugging memory leaks and memoryfragmentation. For more information, see theDevTools documentation.If you’re using an IDE,you can debug your application using the IDE’s debugger.<topic_end><topic_start>How do I perform a hot reload?Flutter’s Stateful Hot Reload feature helps you quickly and easily experiment,build UIs, add features, and fix bugs. Instead of recompiling your appevery time you make a change, you can hot reload your app instantly.The app is updated to reflect your change,and the current state of the app is preserved.In React Native,the shortcut is ⌘R for the iOS Simulator and tapping R twice onAndroid emulators.In Flutter, If you are using IntelliJ IDE or Android Studio,you can select Save All (⌘s/ctrl-s), or you can click theHot Reload button on the toolbar. If youare running the app at the command line using flutter run,type r in the Terminal window.You can also perform a full restart by typing R in theTerminal window.<topic_end><topic_start>How do I access the in-app developer menu?In React Native, the developer menu can be accessed by shaking your device: ⌘Dfor the iOS Simulator or ⌘M for Android emulator.In Flutter, if you are using an IDE, you can use the IDE tools. If you startyour application using flutter run you can also access the menu by typing hin the terminal window, or type the following shortcuts:<topic_end><topic_start>AnimationWell-designed animation makes a UI feel intuitive,contributes to the look and feel of a polished app,and improves the user experience.Flutter’s animation support makes it easyto implement simple and complex animations.The Flutter SDK includes many Material Design widgetsthat include standard motion effects,and you can easily customize these effectsto personalize your app.In React Native, Animated APIs are used to create animations.In Flutter, use the Animationclass and the AnimationController class.Animation is an abstract class that understands itscurrent value and its state (completed or dismissed).The AnimationController class lets youplay an animation forward or in reverse,or stop animation and set the animationto a specific value to customize the motion.<topic_end><topic_start>How do I add a simple fade-in animation?In the React Native example below, an animated component,FadeInView is created using the Animated API.The initial opacity state, final state, and theduration over which the transition occurs are defined.The animation component is added inside the Animated component,the opacity state fadeAnim is mappedto the opacity of the Text component that we want to animate,and then, start() is called to start the animation.To create the same animation in Flutter, create anAnimationController object named controllerand specify the duration. By default, an AnimationControllerlinearly produces values that range from 0.0 to 1.0,during a given duration. The animation controller generates a new valuewhenever the device running your app is ready to display a new frame.Typically, this rate is around 60 values per second.When defining an AnimationController,you must pass in a vsync object.The presence of vsync prevents offscreenanimations from consuming unnecessary resources.You can use your stateful object as the vsync by addingTickerProviderStateMixin to the class definition.An AnimationController needs a TickerProvider,which is configured using the vsync argument on the constructor.A Tween describes the interpolation between abeginning and ending value or the mapping from an inputrange to an output range. To use a Tween objectwith an animation, call the Tween object’s animate()method and pass it the Animation object that you want to modify.For this example, a FadeTransitionwidget is used and the opacity property ismapped to the animation object.To start the animation, use controller.forward().Other operations can also be performed using thecontroller such as fling() or repeat().For this example, the FlutterLogowidget is used inside the FadeTransition widget.<code_start>import 'package:flutter/material.dart';void main() {  runApp(const Center(child: LogoFade()));}class LogoFade extends StatefulWidget {  const LogoFade({super.key});  @override  State<LogoFade> createState() => _LogoFadeState();}class _LogoFadeState extends State<LogoFade>    with SingleTickerProviderStateMixin {  late Animation<double> animation;  late AnimationController controller;  @override  void initState() {    super.initState();    controller = AnimationController(      duration: const Duration(milliseconds: 3000),      vsync: this,    );    final CurvedAnimation curve = CurvedAnimation(      parent: controller,      curve: Curves.easeIn,    );    animation = Tween(begin: 0.0, end: 1.0).animate(curve);    controller.forward();  }  @override  void dispose() {    controller.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    return FadeTransition(      opacity: animation,      child: const SizedBox(        height: 300,        width: 300,        child: FlutterLogo(),      ),    );  }}<code_end><topic_end><topic_start>How do I add swipe animation to cards?In React Native, either the PanResponder orthird-party libraries are used for swipe animation.In Flutter, to add a swipe animation, use theDismissible widget and nest the child widgets.<code_start>return Dismissible(  key: Key(widget.key.toString()),  onDismissed: (dismissDirection) {    cards.removeLast();  },  child: Container(      //...      ),);<code_end><topic_end><topic_start>React Native and Flutter widget equivalent componentsThe following table lists commonly-used React Nativecomponents mapped to the corresponding Flutter widgetand common widget properties.<topic_end><topic_start>Flutter for web developersThis page is for users who are familiar with the HTMLand CSS syntax for arranging components of an application’s UI.It maps HTML/CSS code snippets to their Flutter/Dart code equivalents.Flutter is a framework for building cross-platform applicationsthat uses the Dart programming language.To understand some differences between programming with Dartand programming with Javascript, see Learning Dart as a JavaScript Developer.One of the fundamental differences betweendesigning a web layout and a Flutter layout,is learning how constraints work,and how widgets are sized and positioned.To learn more, see Understanding constraints.The examples assume:The HTML document starts with <!DOCTYPE html>, and the CSS box modelfor all HTML elements is set to border-box,for consistency with the Flutter model.In Flutter, the default styling of the ‘Lorem ipsum’ textis defined by the bold24Roboto variable as follows,to keep the syntax simple:<code_start>TextStyle bold24Roboto = const TextStyle(  color: Colors.white,  fontSize: 24,  fontWeight: FontWeight.bold,);<code_end>How is react-style, or declarative, programming different from the  traditional imperative style?  For a comparison, see Introduction to declarative UI.<topic_end><topic_start>Performing basic layout operationsThe following examples show how to perform the most common UI layout tasks.<topic_end><topic_start>Styling and aligning textFont style, size, and other text attributes that CSShandles with the font and color properties are individualproperties of a TextStyle child of a Text widget.For text-align property in CSS that is used for aligning text,there is a textAlign property of a Text widget.In both HTML and Flutter, child elements or widgetsare anchored at the top left, by default.<topic_end><topic_start>Setting background colorIn Flutter, you set the background color using the color propertyor the decoration property of a Container.However, you cannot supply both, since it would potentiallyresult in the decoration drawing over the background color.The color property should be preferredwhen the background is a simple color.For other cases, such as gradients or images,use the decoration property.The CSS examples use the hex color equivalents to the Material color palette.<topic_end><topic_start>Centering componentsA Center widget centers its child both horizontallyand vertically.To accomplish a similar effect in CSS, the parent element uses either a flexor table-cell display behavior. The examples on this page show the flexbehavior.<topic_end><topic_start>Setting container widthTo specify the width of a Containerwidget, use its width property.This is a fixed width, unlike the CSS max-width propertythat adjusts the container width up to a maximum value.To mimic that effect in Flutter,use the constraints property of the Container.Create a new BoxConstraints widget with a minWidth or maxWidth.For nested Containers, if the parent’s width is less than the child’s width,the child Container sizes itself to match the parent.<topic_end><topic_start>Manipulating position and sizeThe following examples show how to perform more complex operationson widget position, size, and background.<topic_end><topic_start>Setting absolute positionBy default, widgets are positioned relative to their parent.To specify an absolute position for a widget as x-y coordinates,nest it in a Positioned widget that is,in turn, nested in a Stack widget.<topic_end><topic_start>Rotating componentsTo rotate a widget, nest it in a Transform widget.Use the Transform widget’s alignment and origin propertiesto specify the transform origin (fulcrum) in relative and absolute terms,respectively.For a simple 2D rotation, in which the widget is rotated on the Z axis,create a new Matrix4 identity objectand use its rotateZ() method to specify the rotation factorusing radians (degrees × π / 180).<topic_end><topic_start>Scaling componentsTo scale a widget up or down, nest it in a Transform widget.Use the Transform widget’s alignment and origin propertiesto specify the transform origin (fulcrum) in relative or absolute terms,respectively.For a simple scaling operation along the x-axis,create a new Matrix4 identity objectand use its scale() method to specify the scaling factor.When you scale a parent widget,its child widgets are scaled accordingly.<topic_end><topic_start>Applying a linear gradientTo apply a linear gradient to a widget’s background,nest it in a Container widget.Then use the Container widget’s decoration property to create aBoxDecoration object, and use BoxDecoration’s gradientproperty to transform the background fill.The gradient “angle” is based on the Alignment (x, y) values:<topic_end><topic_start>Vertical gradient<topic_end><topic_start>Horizontal gradient<topic_end><topic_start>Manipulating shapesThe following examples show how to make and customize shapes.<topic_end><topic_start>Rounding cornersTo round the corners of a rectangular shape,use the borderRadius property of a BoxDecoration object.Create a new BorderRadiusobject that specifies the radius for rounding each corner.<topic_end><topic_start>Adding box shadowsIn CSS you can specify shadow offset and blur in shorthand,using the box-shadow property. This example shows two box shadows,with properties:In Flutter, each property and value is specified separately.Use the boxShadow property of BoxDecoration to create a list ofBoxShadow widgets. You can define one or multipleBoxShadow widgets, which can be stackedto customize the shadow depth, color, and so on.<topic_end><topic_start>Making circles and ellipsesMaking a circle in CSS requires a workaround of applying aborder-radius of 50% to all four sides of a rectangle,though there are basic shapes.While this approach is supportedwith the borderRadius property of BoxDecoration,Flutter provides a shape propertywith BoxShape enum for this purpose.<topic_end><topic_start>Manipulating textThe following examples show how to specify fonts and othertext attributes. They also show how to transform text strings,customize spacing, and create excerpts.<topic_end><topic_start>Adjusting text spacingIn CSS, you specify the amount of white spacebetween each letter or word by giving a length valuefor the letter-spacing and word-spacing properties, respectively.The amount of space can be in px, pt, cm, em, etc.In Flutter, you specify white space as logical pixels(negative values are allowed)for the letterSpacing and wordSpacing propertiesof a TextStyle child of a Text widget.<topic_end><topic_start>Making inline formatting changesA Text widget lets you display textwith some formatting characteristics.To display text that uses multiple styles(in this example, a single word with emphasis),use a RichText widget instead.Its text property can specify one or moreTextSpan objects that can be individually styled.In the following example, “Lorem” is in a TextSpanwith the default (inherited) text styling,and “ipsum” is in a separate TextSpan with custom styling.<topic_end><topic_start>Creating text excerptsAn excerpt displays the initial line(s) of text in a paragraph,and handles the overflow text, often using an ellipsis.In Flutter, use the maxLines property of a Text widgetto specify the number of lines to include in the excerpt,and the overflow property for handling overflow text.<topic_end><topic_start>Flutter for Xamarin.Forms developersThis document is meant for Xamarin.Forms developerslooking to apply their existing knowledgeto build mobile apps with Flutter.If you understand the fundamentals of the Xamarin.Forms framework,then you can use this document as a jump start to Flutter development.Your Android and iOS knowledge and skill setare valuable when building with Flutter,because Flutter relies on the native operating system configurations,similar to how you would configure your native Xamarin.Forms projects.The Flutter Frameworks is also similar to how you create a single UI,that is used on multiple platforms.This document can be used as a cookbook by jumping aroundand finding questions that are most relevant to your needs.<topic_end><topic_start>Project setup<topic_end><topic_start>How does the app start?For each platform in Xamarin.Forms,you call the LoadApplication method,which creates a new application and starts your app.In Flutter, the default main entry point ismain where you load your Flutter app.<code_start>void main() {  runApp(const MyApp());}<code_end>In Xamarin.Forms, you assign a Page to theMainPage property in the Application class.In Flutter, “everything is a widget”, even the application itself.The following example shows MyApp, a simple application Widget.<code_start>class MyApp extends StatelessWidget {  /// This widget is the root of your application.  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return const Center(      child: Text(        'Hello World!',        textDirection: TextDirection.ltr,      ),    );  }}<code_end><topic_end><topic_start>How do you create a page?Xamarin.Forms has many types of pages;ContentPage is the most common.In Flutter, you specify an application widget that holds your root page.You can use a MaterialApp widget, which supports Material Design,or you can use a CupertinoApp widget, which supports an iOS-style app,or you can use the lower level WidgetsApp,which you can customize in any way you want.The following code defines the home page, a stateful widget.In Flutter, all widgets are immutable,but two types of widgets are supported: Stateful and Stateless.Examples of a stateless widget are titles, icons, or images.The following example uses MaterialApp,which holds its root page in the home property.<code_start>class MyApp extends StatelessWidget {  /// This widget is the root of your application.  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Flutter Demo',      home: MyHomePage(title: 'Flutter Demo Home Page'),    );  }}<code_end>From here, your actual first page is another Widget,in which you create your state.A Stateful widget, such as MyHomePage below, consists of two parts.The first part, which is itself immutable, creates a State objectthat holds the state of the object. The State object persists overthe life of the widget.<code_start>class MyHomePage extends StatefulWidget {  const MyHomePage({super.key, required this.title});  final String title;  @override  State<MyHomePage> createState() => _MyHomePageState();}<code_end>The State object implements the build() method for the stateful widget.When the state of the widget tree changes, call setState(),which triggers a build of that portion of the UI.Make sure to call setState() only when necessary,and only on the part of the widget tree that has changed,or it can result in poor UI performance.<code_start>class _MyHomePageState extends State<MyHomePage> {  int _counter = 0;  void _incrementCounter() {    setState(() {      _counter++;    });  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        // Take the value from the MyHomePage object that was created by        // the App.build method, and use it to set the appbar title.        title: Text(widget.title),      ),      body: Center(        // Center is a layout widget. It takes a single child and positions it        // in the middle of the parent.        child: Column(          mainAxisAlignment: MainAxisAlignment.center,          children: <Widget>[            const Text(              'You have pushed the button this many times:',            ),            Text(              '$_counter',              style: Theme.of(context).textTheme.headlineMedium,            ),          ],        ),      ),      floatingActionButton: FloatingActionButton(        onPressed: _incrementCounter,        tooltip: 'Increment',        child: const Icon(Icons.add),      ),    );  }}<code_end>In Flutter, the UI (also known as widget tree), is immutable,meaning you can’t change its state once it’s built.You change fields in your State class, then call setState()to rebuild the entire widget tree again.This way of generating UI is different from Xamarin.Forms,but there are many benefits to this approach.<topic_end><topic_start>Views<topic_end><topic_start>What is the equivalent of a Page or Element in Flutter?How is react-style, or declarative, programming different from the  traditional imperative style?  For a comparison, see Introduction to declarative UI.ContentPage, TabbedPage, FlyoutPage are all types of pagesyou might use in a Xamarin.Forms application.These pages would then hold Elements to display the various controls.In Xamarin.Forms an Entry or Button are examples of an Element.In Flutter, almost everything is a widget.A Page, called a Route in Flutter, is a widget.Buttons, progress bars, and animation controllers are all widgets.When building a route, you create a widget tree.Flutter includes the Material Components library.These are widgets that implement the Material Design guidelines.Material Design is a flexible design systemoptimized for all platforms, including iOS.But Flutter is flexible and expressive enoughto implement any design language.For example, on iOS, you can use the Cupertino widgetsto produce an interface that looks like Apple’s iOS design language.<topic_end><topic_start>How do I update widgets?In Xamarin.Forms, each Page or Element is a stateful class,that has properties and methods.You update your Element by updating a property,and this is propagated down to the native control.In Flutter, Widgets are immutable and you can’t directly update themby changing a property, instead you have to work with the widget’s state.This is where the concept of Stateful vs Stateless widgets comes from.A StatelessWidget is just what it sounds like—a widget with no state information.StatelessWidgets are useful when the part of the user interfaceyou are describing doesn’t depend on anythingother than the configuration information in the object.For example, in Xamarin.Forms, this is similarto placing an Image with your logo.The logo is not going to change during runtime,so use a StatelessWidget in Flutter.If you want to dynamically change the UI based on data receivedafter making an HTTP call or a user interaction,then you have to work with StatefulWidgetand tell the Flutter framework thatthe widget’s State has been updated,so it can update that widget.The important thing to note here is at the coreboth stateless and stateful widgets behave the same.They rebuild every frame, the difference isthe StatefulWidget has a State objectthat stores state data across frames and restores it.If you are in doubt, then always remember this rule: if a widget changes(because of user interactions, for example) it’s stateful.However, if a widget reacts to change, the containing parent widget canstill be stateless if it doesn’t itself react to change.The following example shows how to use a StatelessWidget.A common StatelessWidget is the Text widget.If you look at the implementation of the Text widgetyou’ll find it subclasses StatelessWidget.<code_start>const Text(  'I like Flutter!',  style: TextStyle(fontWeight: FontWeight.bold),);<code_end>As you can see, the Text widget has no state information associated with it,it renders what is passed in its constructors and nothing more.But, what if you want to make “I Like Flutter” change dynamically,for example, when clicking a FloatingActionButton?To achieve this, wrap the Text widget in a StatefulWidgetand update it when the user clicks the button,as shown in the following example:<code_start>import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  /// This widget is the root of your application.  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  /// Default placeholder text  String textToShow = 'I Like Flutter';  void _updateText() {    setState(() {      // Update the text      textToShow = 'Flutter is Awesome!';    });  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Sample App')),      body: Center(child: Text(textToShow)),      floatingActionButton: FloatingActionButton(        onPressed: _updateText,        tooltip: 'Update Text',        child: const Icon(Icons.update),      ),    );  }}<code_end><topic_end><topic_start>How do I lay out my widgets? What is the equivalent of an XAML file?In Xamarin.Forms, most developers write layouts in XAML,though sometimes in C#.In Flutter, you write your layouts with a widget tree in code.The following example shows how to display a simple widget with padding:<code_start>@overrideWidget build(BuildContext context) {  return Scaffold(    appBar: AppBar(title: const Text('Sample App')),    body: Center(      child: ElevatedButton(        style: ElevatedButton.styleFrom(          padding: const EdgeInsets.only(left: 20, right: 30),        ),        onPressed: () {},        child: const Text('Hello'),      ),    ),  );}<code_end>You can view the layouts that Flutter has to offer in thewidget catalog.<topic_end><topic_start>How do I add or remove an Element from my layout?In Xamarin.Forms, you had to remove or add an Element in code.This involved either setting the Content property or callingAdd() or Remove() if it was a list.In Flutter, because widgets are immutable there is no direct equivalent.Instead, you can pass a function to the parent that returns a widget,and control that child’s creation with a boolean flag.The following example shows how to toggle between two widgetswhen the user clicks the FloatingActionButton:<code_start>class SampleApp extends StatelessWidget {  /// This widget is the root of your application.  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  /// Default value for toggle  bool toggle = true;  void _toggle() {    setState(() {      toggle = !toggle;    });  }  Widget _getToggleChild() {    if (toggle) {      return const Text('Toggle One');    }    return CupertinoButton(      onPressed: () {},      child: const Text('Toggle Two'),    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Sample App')),      body: Center(child: _getToggleChild()),      floatingActionButton: FloatingActionButton(        onPressed: _toggle,        tooltip: 'Update Text',        child: const Icon(Icons.update),      ),    );  }}<code_end><topic_end><topic_start>How do I animate a widget?In Xamarin.Forms, you create simple animations using ViewExtensions thatinclude methods such as FadeTo and TranslateTo.You would use these methods on a viewto perform the required animations.Then in code behind, or a behavior, this would fade in the image,over a 1-second period.In Flutter, you animate widgets using the animation libraryby wrapping widgets inside an animated widget.Use an AnimationController, which is an Animation<double>that can pause, seek, stop and reverse the animation.It requires a Ticker that signals when vsync happens,and produces a linear interpolation between 0 and 1on each frame while it’s running.You then create one or moreAnimations and attach them to the controller.For example, you might use CurvedAnimationto implement an animation along an interpolated curve.In this sense, the controller is the “master” source of the animation progressand the CurvedAnimation computes the curvethat replaces the controller’s default linear motion.Like widgets, animations in Flutter work with composition.When building the widget tree, you assign the Animationto an animated property of a widget,such as the opacity of a FadeTransition,and tell the controller to start the animation.The following example shows how to write a FadeTransition that fadesthe widget into a logo when you press the FloatingActionButton:<code_start>import 'package:flutter/material.dart';void main() {  runApp(const FadeAppTest());}class FadeAppTest extends StatelessWidget {  /// This widget is the root of your application.  const FadeAppTest({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Fade Demo',      home: MyFadeTest(title: 'Fade Demo'),    );  }}class MyFadeTest extends StatefulWidget {  const MyFadeTest({super.key, required this.title});  final String title;  @override  State<MyFadeTest> createState() => _MyFadeTest();}class _MyFadeTest extends State<MyFadeTest> with TickerProviderStateMixin {  late final AnimationController controller;  late final CurvedAnimation curve;  @override  void initState() {    super.initState();    controller = AnimationController(      duration: const Duration(milliseconds: 2000),      vsync: this,    );    curve = CurvedAnimation(      parent: controller,      curve: Curves.easeIn,    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: Text(widget.title)),      body: Center(        child: FadeTransition(          opacity: curve,          child: const FlutterLogo(size: 100),        ),      ),      floatingActionButton: FloatingActionButton(        onPressed: () {          controller.forward();        },        tooltip: 'Fade',        child: const Icon(Icons.brush),      ),    );  }}<code_end>For more information, see Animation & Motion widgets,the Animations tutorial, and the Animations overview.<topic_end><topic_start>How do I draw/paint on the screen?Xamarin.Forms never had a built-in way to draw directly on the screen.Many would use SkiaSharp, if they needed a custom image drawn.In Flutter, you have direct access to the Skia Canvasand can easily draw on screen.Flutter has two classes that help you draw to the canvas: CustomPaintand CustomPainter, the latter of which implements your algorithm to draw tothe canvas.To learn how to implement a signature painter in Flutter,see Collin’s answer on Custom Paint.<code_start>import 'package:flutter/material.dart';void main() {  runApp(const MaterialApp(home: DemoApp()));}class DemoApp extends StatelessWidget {  const DemoApp({super.key});  @override  Widget build(BuildContext context) => const Scaffold(body: Signature());}class Signature extends StatefulWidget {  const Signature({super.key});  @override  SignatureState createState() => SignatureState();}class SignatureState extends State<Signature> {  List<Offset?> _points = <Offset?>[];  void _onPanUpdate(DragUpdateDetails details) {    setState(() {      final RenderBox referenceBox = context.findRenderObject() as RenderBox;      final Offset localPosition = referenceBox.globalToLocal(        details.globalPosition,      );      _points = List.from(_points)..add(localPosition);    });  }  @override  Widget build(BuildContext context) {    return GestureDetector(      onPanUpdate: _onPanUpdate,      onPanEnd: (details) => _points.add(null),      child: CustomPaint(        painter: SignaturePainter(_points),        size: Size.infinite,      ),    );  }}class SignaturePainter extends CustomPainter {  const SignaturePainter(this.points);  final List<Offset?> points;  @override  void paint(Canvas canvas, Size size) {    final Paint paint = Paint()      ..color = Colors.black      ..strokeCap = StrokeCap.round      ..strokeWidth = 5;    for (int i = 0; i < points.length - 1; i++) {      if (points[i] != null && points[i + 1] != null) {        canvas.drawLine(points[i]!, points[i + 1]!, paint);      }    }  }  @override  bool shouldRepaint(SignaturePainter oldDelegate) =>      oldDelegate.points != points;}<code_end><topic_end><topic_start>Where is the widget’s opacity?On Xamarin.Forms, all VisualElements have an Opacity.In Flutter, you need to wrap a widget in anOpacity widget to accomplish this.<topic_end><topic_start>How do I build custom widgets?In Xamarin.Forms, you typically subclass VisualElement,or use a pre-existing VisualElement, to override andimplement methods that achieve the desired behavior.In Flutter, build a custom widget by composingsmaller widgets (instead of extending them).It is somewhat similar to implementing a custom controlbased off a Grid with numerous VisualElements added in,while extending with custom logic.For example, how do you build a CustomButtonthat takes a label in the constructor?Create a CustomButton that composes a ElevatedButtonwith a label, rather than by extending ElevatedButton:<code_start>class CustomButton extends StatelessWidget {  const CustomButton(this.label, {super.key});  final String label;  @override  Widget build(BuildContext context) {    return ElevatedButton(      onPressed: () {},      child: Text(label),    );  }}<code_end>Then use CustomButton, just as you’d use any other Flutter widget:<code_start>@overrideWidget build(BuildContext context) {  return const Center(    child: CustomButton('Hello'),  );}<code_end><topic_end><topic_start>Navigation<topic_end><topic_start>How do I navigate between pages?In Xamarin.Forms, the NavigationPage classprovides a hierarchical navigation experiencewhere the user is able to navigate through pages,forwards and backwards.Flutter has a similar implementation,using a Navigator and Routes.A Route is an abstraction for a Page of an app,and a Navigator is a widget that manages routes.A route roughly maps to a Page.The navigator works in a similar way to the Xamarin.Forms NavigationPage,in that it can push() and pop() routes depending onwhether you want to navigate to, or back from, a view.To navigate between pages, you have a couple options:The following example builds a Map.<code_start>void main() {  runApp(    MaterialApp(      home: const MyAppHome(), // becomes the route named '/'      routes: <String, WidgetBuilder>{        '/a': (context) => const MyPage(title: 'page A'),        '/b': (context) => const MyPage(title: 'page B'),        '/c': (context) => const MyPage(title: 'page C'),      },    ),  );}<code_end>Navigate to a route by pushing its name to the Navigator.<code_start>Navigator.of(context).pushNamed('/b');<code_end>The Navigator is a stack that manages your app’s routes.Pushing a route to the stack moves to that route.Popping a route from the stack, returns to the previous route.This is done by awaiting on the Future returned by push().async/await is very similar to the .NET implementationand is explained in more detail in Async UI.For example, to start a location routethat lets the user select their location,you might do the following:<code_start>Object? coordinates = await Navigator.of(context).pushNamed('/location');<code_end>And then, inside your ‘location’ route, once the user has selected theirlocation, pop the stack with the result:<code_start>Navigator.of(context).pop({'lat': 43.821757, 'long': -79.226392});<code_end><topic_end><topic_start>How do I navigate to another app?In Xamarin.Forms, to send the user to another application,you use a specific URI scheme, using Device.OpenUrl("mailto://").To implement this functionality in Flutter,create a native platform integration, or use an existing plugin,such asurl_launcher, available with many other packages on pub.dev.<topic_end><topic_start>Async UI<topic_end><topic_start>What is the equivalent of Device.BeginOnMainThread() in Flutter?Dart has a single-threaded execution model,with support for Isolates (a way to run Dart codes on another thread),an event loop, and asynchronous programming.Unless you spawn an Isolate,your Dart code runs in the main UI threadand is driven by an event loop.Dart’s single-threaded model doesn’t mean you need to run everythingas a blocking operation that causes the UI to freeze.Much like Xamarin.Forms, you need to keep the UI thread free.You would use async/await to perform tasks,where you must wait for the response.In Flutter, use the asynchronous facilities that the Dart language provides,also named async/await, to perform asynchronous work.This is very similar to C# and should be very easy to usefor any Xamarin.Forms developer.For example, you can run network code without causing the UI to hang byusing async/await and letting Dart do the heavy lifting:<code_start>Future<void> loadData() async {  final Uri dataURL = Uri.parse(    'https://jsonplaceholder.typicode.com/posts',  );  final http.Response response = await http.get(dataURL);  setState(() {    data = jsonDecode(response.body);  });}<code_end>Once the awaited network call is done,update the UI by calling setState(),which triggers a rebuild of the widget subtree and updates the data.The following example loads data asynchronouslyand displays it in a ListView:<code_start>import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List<Map<String, dynamic>> data = <Map<String, dynamic>>[];  @override  void initState() {    super.initState();    loadData();  }  Future<void> loadData() async {    final Uri dataURL = Uri.parse(      'https://jsonplaceholder.typicode.com/posts',    );    final http.Response response = await http.get(dataURL);    setState(() {      data = jsonDecode(response.body);    });  }  Widget getRow(int index) {    return Padding(      padding: const EdgeInsets.all(10),      child: Text('Row ${data[index]['title']}'),    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Sample App')),      body: ListView.builder(        itemCount: data.length,        itemBuilder: (context, index) {          return getRow(index);        },      ),    );  }}<code_end>Refer to the next section for more informationon doing work in the background,and how Flutter differs from Android.<topic_end><topic_start>How do you move work to a background thread?Since Flutter is single threaded and runs an event loop,you don’t have to worry about thread managementor spawning background threads.This is very similar to Xamarin.Forms.If you’re doing I/O-bound work, such as disk access or a network call,then you can safely use async/await and you’re all set.If, on the other hand, you need to do computationally intensive workthat keeps the CPU busy,you want to move it to an Isolate to avoid blocking the event loop,like you would keep any sort of work out of the main thread.This is similar to when you move things to a differentthread via Task.Run() in Xamarin.Forms.For I/O-bound work, declare the function as an async function,and await on long-running tasks inside the function:<code_start>Future<void> loadData() async {  final Uri dataURL = Uri.parse(    'https://jsonplaceholder.typicode.com/posts',  );  final http.Response response = await http.get(dataURL);  setState(() {    data = jsonDecode(response.body);  });}<code_end>This is how you would typically do network or database calls,which are both I/O operations.However, there are times when you might be processinga large amount of data and your UI hangs.In Flutter, use Isolates to take advantage of multiple CPU coresto do long-running or computationally intensive tasks.Isolates are separate execution threads thatdo not share any memory with the main execution memory heap.This is a difference between Task.Run().This means you can’t access variables from the main thread,or update your UI by calling setState().The following example shows, in a simple isolate,how to share data back to the main thread to update the UI.<code_start>Future<void> loadData() async {  final ReceivePort receivePort = ReceivePort();  await Isolate.spawn(dataLoader, receivePort.sendPort);  // The 'echo' isolate sends its SendPort as the first message  final SendPort sendPort = await receivePort.first as SendPort;  final List<Map<String, dynamic>> msg = await sendReceive(    sendPort,    'https://jsonplaceholder.typicode.com/posts',  );  setState(() {    data = msg;  });}// The entry point for the isolatestatic Future<void> dataLoader(SendPort sendPort) async {  // Open the ReceivePort for incoming messages.  final ReceivePort port = ReceivePort();  // Notify any other isolates what port this isolate listens to.  sendPort.send(port.sendPort);  await for (final dynamic msg in port) {    final String url = msg[0] as String;    final SendPort replyTo = msg[1] as SendPort;    final Uri dataURL = Uri.parse(url);    final http.Response response = await http.get(dataURL);    // Lots of JSON to parse    replyTo.send(jsonDecode(response.body) as List<Map<String, dynamic>>);  }}Future<List<Map<String, dynamic>>> sendReceive(SendPort port, String msg) {  final ReceivePort response = ReceivePort();  port.send(<dynamic>[msg, response.sendPort]);  return response.first as Future<List<Map<String, dynamic>>>;}<code_end>Here, dataLoader() is the Isolate that runs inits own separate execution thread.In the isolate, you can perform more CPU intensiveprocessing (parsing a big JSON, for example),or perform computationally intensive math,such as encryption or signal processing.You can run the full example below:<code_start>import 'dart:async';import 'dart:convert';import 'dart:isolate';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List<Map<String, dynamic>> data = <Map<String, dynamic>>[];  @override  void initState() {    super.initState();    loadData();  }  bool get showLoadingDialog => data.isEmpty;  Future<void> loadData() async {    final ReceivePort receivePort = ReceivePort();    await Isolate.spawn(dataLoader, receivePort.sendPort);    // The 'echo' isolate sends its SendPort as the first message    final SendPort sendPort = await receivePort.first as SendPort;    final List<Map<String, dynamic>> msg = await sendReceive(      sendPort,      'https://jsonplaceholder.typicode.com/posts',    );    setState(() {      data = msg;    });  }  // The entry point for the isolate  static Future<void> dataLoader(SendPort sendPort) async {    // Open the ReceivePort for incoming messages.    final ReceivePort port = ReceivePort();    // Notify any other isolates what port this isolate listens to.    sendPort.send(port.sendPort);    await for (final dynamic msg in port) {      final String url = msg[0] as String;      final SendPort replyTo = msg[1] as SendPort;      final Uri dataURL = Uri.parse(url);      final http.Response response = await http.get(dataURL);      // Lots of JSON to parse      replyTo.send(jsonDecode(response.body) as List<Map<String, dynamic>>);    }  }  Future<List<Map<String, dynamic>>> sendReceive(SendPort port, String msg) {    final ReceivePort response = ReceivePort();    port.send(<dynamic>[msg, response.sendPort]);    return response.first as Future<List<Map<String, dynamic>>>;  }  Widget getBody() {    if (showLoadingDialog) {      return getProgressDialog();    }    return getListView();  }  Widget getProgressDialog() {    return const Center(child: CircularProgressIndicator());  }  ListView getListView() {    return ListView.builder(      itemCount: data.length,      itemBuilder: (context, index) {        return getRow(index);      },    );  }  Widget getRow(int index) {    return Padding(      padding: const EdgeInsets.all(10),      child: Text('Row ${data[index]['title']}'),    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Sample App')),      body: getBody(),    );  }}<code_end><topic_end><topic_start>How do I make network requests?In Xamarin.Forms you would use HttpClient.Making a network call in Flutter is easywhen you use the popular http package.This abstracts away a lot of the networkingthat you might normally implement yourself,making it simple to make network calls.To use the http package, add it to your dependencies in pubspec.yaml:To make a network request,call await on the async function http.get():<code_start>Future<void> loadData() async {  final Uri dataURL = Uri.parse(    'https://jsonplaceholder.typicode.com/posts',  );  final http.Response response = await http.get(dataURL);  setState(() {    data = jsonDecode(response.body);  });}<code_end><topic_end><topic_start>How do I show the progress for a long-running task?In Xamarin.Forms you would typically create a loading indicator,either directly in XAML or through a 3rd party plugin such as AcrDialogs.In Flutter, use a ProgressIndicator widget.Show the progress programmatically by controllingwhen it’s rendered through a boolean flag.Tell Flutter to update its state before your long-running task starts,and hide it after it ends.In the example below, the build function is separated into three differentfunctions. If showLoadingDialog is true(when widgets.length == 0), then render the ProgressIndicator.Otherwise, render the ListView with the data returned from a network call.<code_start>import 'dart:async';import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List<Map<String, dynamic>> data = <Map<String, dynamic>>[];  @override  void initState() {    super.initState();    loadData();  }  bool get showLoadingDialog => data.isEmpty;  Future<void> loadData() async {    final Uri dataURL = Uri.parse(      'https://jsonplaceholder.typicode.com/posts',    );    final http.Response response = await http.get(dataURL);    setState(() {      data = jsonDecode(response.body);    });  }  Widget getBody() {    if (showLoadingDialog) {      return getProgressDialog();    }    return getListView();  }  Widget getProgressDialog() {    return const Center(child: CircularProgressIndicator());  }  ListView getListView() {    return ListView.builder(      itemCount: data.length,      itemBuilder: (context, index) {        return getRow(index);      },    );  }  Widget getRow(int index) {    return Padding(      padding: const EdgeInsets.all(10),      child: Text('Row ${data[index]['title']}'),    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Sample App')),      body: getBody(),    );  }}<code_end><topic_end><topic_start>Project structure & resources<topic_end><topic_start>Where do I store my image files?Xamarin.Forms has no platform independent way of storing images,you had to place images in the iOS xcasset folder,or on Android in the various drawable folders.While Android and iOS treat resources and assets as distinct items,Flutter apps have only assets.All resources that would live in theResources/drawable-* folders on Android,are placed in an assets’ folder for Flutter.Flutter follows a simple density-based format like iOS.Assets might be 1.0x, 2.0x, 3.0x, or any other multiplier.Flutter doesn’t have dps but there are logical pixels,which are basically the same as device-independent pixels.Flutter’s devicePixelRatio expresses the ratioof physical pixels in a single logical pixel.The equivalent to Android’s density buckets are:Assets are located in any arbitrary folder—Flutter has no predefined folder structure.You declare the assets (with location)in the pubspec.yaml file, and Flutter picks them up.To add a new image asset called my_icon.png to our Flutter project,for example, and deciding that it should live in a folder wearbitrarily called images, you would put the base image (1.0x)in the images folder, and all the other variants in sub-folderscalled with the appropriate ratio multiplier:Next, you’ll need to declare these images in your pubspec.yaml file:You can directly access your images in an Image.asset widget:<code_start>@overrideWidget build(BuildContext context) {  return Image.asset('images/my_icon.png');}<code_end>or using AssetImage:<code_start>@overrideWidget build(BuildContext context) {  return const Image(    image: AssetImage('images/my_image.png'),  );}<code_end>More detailed information can be found in Adding assets and images.<topic_end><topic_start>Where do I store strings? How do I handle localization?Unlike .NET which has resx files,Flutter doesn’t currently have a dedicated system for handling strings.At the moment, the best practice is to declare your copy textin a class as static fields and access them from there. For example:<code_start>class Strings {  static const String welcomeMessage = 'Welcome To Flutter';}<code_end>You can access your strings as such:<code_start>Text(Strings.welcomeMessage);<code_end>By default, Flutter only supports US English for its strings.If you need to add support for other languages,include the flutter_localizations package.You might also need to add Dart’s intlpackage to use i10n machinery, such as date/time formatting.To use the flutter_localizations package,specify the localizationsDelegates andsupportedLocales on the app widget:<code_start>import 'package:flutter_localizations/flutter_localizations.dart';class MyWidget extends StatelessWidget {  const MyWidget({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      localizationsDelegates: <LocalizationsDelegate<dynamic>>[        // Add app-specific localization delegate[s] here        GlobalMaterialLocalizations.delegate,        GlobalWidgetsLocalizations.delegate,      ],      supportedLocales: <Locale>[        Locale('en', 'US'), // English        Locale('he', 'IL'), // Hebrew        // ... other locales the app supports      ],    );  }}<code_end>The delegates contain the actual localized values,while the supportedLocales defines which locales the app supports.The above example uses a MaterialApp,so it has both a GlobalWidgetsLocalizationsfor the base widgets localized values,and a MaterialWidgetsLocalizations for the Material widgets localizations.If you use WidgetsApp for your app, you don’t need the latter.Note that these two delegates contain “default” values,but you’ll need to provide one or more delegatesfor your own app’s localizable copy,if you want those to be localized too.When initialized, the WidgetsApp (or MaterialApp)creates a Localizations widget for you,with the delegates you specify.The current locale for the device is always accessiblefrom the Localizations widget from the current context(in the form of a Locale object), or using the Window.locale.To access localized resources, use the Localizations.of() methodto access a specific localizations class that is provided by a given delegate.Use the intl_translation package to extract translatable copyto arb files for translating, and importing them back into the appfor using them with intl.For further details on internationalization and localization in Flutter,see the internationalization guide, which has sample codewith and without the intl package.<topic_end><topic_start>Where is my project file?In Xamarin.Forms you will have a csproj file.The closest equivalent in Flutter is pubspec.yaml,which contains package dependencies and various project details.Similar to .NET Standard,files within the same directory are considered part of the project.<topic_end><topic_start>What is the equivalent of Nuget? How do I add dependencies?In the .NET ecosystem, native Xamarin projects and Xamarin.Forms projectshad access to Nuget and the built-in package management system.Flutter apps contain a native Android app, native iOS app and Flutter app.In Android, you add dependencies by adding to your Gradle build script.In iOS, you add dependencies by adding to your Podfile.Flutter uses Dart’s own build system, and the Pub package manager.The tools delegate the building of the native Android and iOS wrapper appsto the respective build systems.In general, use pubspec.yaml to declareexternal dependencies to use in Flutter.A good place to find Flutter packages is on pub.dev.<topic_end><topic_start>Application lifecycle<topic_end><topic_start>How do I listen to application lifecycle events?In Xamarin.Forms, you have an Applicationthat contains OnStart, OnResume and OnSleep.In Flutter, you can instead listen to similar lifecycle eventsby hooking into the WidgetsBinding observer and listening tothe didChangeAppLifecycleState() change event.The observable lifecycle events are:For more details on the meaning of these states,see the AppLifecycleStatus documentation.<topic_end><topic_start>Layouts<topic_end><topic_start>What is the equivalent of a StackLayout?In Xamarin.Forms you can create a StackLayoutwith an Orientation of horizontal or vertical.Flutter has a similar approach,however you would use the Row or Column widgets.If you notice the two code samples are identicalexcept the Row and Column widget.The children are the same and this featurecan be exploited to develop rich layoutsthat can change overtime with the same children.<code_start>@overrideWidget build(BuildContext context) {  return const Row(    mainAxisAlignment: MainAxisAlignment.center,    children: <Widget>[      Text('Row One'),      Text('Row Two'),      Text('Row Three'),      Text('Row Four'),    ],  );}<code_end><code_start>@overrideWidget build(BuildContext context) {  return const Column(    mainAxisAlignment: MainAxisAlignment.center,    children: <Widget>[      Text('Column One'),      Text('Column Two'),      Text('Column Three'),      Text('Column Four'),    ],  );<code_end><topic_end><topic_start>What is the equivalent of a Grid?The closest equivalent of a Grid would be a GridView.This is much more powerful than what you are used to in Xamarin.Forms.A GridView provides automatic scrolling when thecontent exceeds its viewable space.<code_start>@overrideWidget build(BuildContext context) {  return GridView.count(    // Create a grid with 2 columns. If you change the scrollDirection to    // horizontal, this would produce 2 rows.    crossAxisCount: 2,    // Generate 100 widgets that display their index in the list.    children: List<Widget>.generate(      100,      (index) {        return Center(          child: Text(            'Item $index',            style: Theme.of(context).textTheme.headlineMedium,          ),        );      },    ),  );}<code_end>You might have used a Grid in Xamarin.Formsto implement widgets that overlay other widgets.In Flutter, you accomplish this with the Stack widget.This sample creates two icons that overlap each other.<code_start>@overrideWidget build(BuildContext context) {  return const Stack(    children: <Widget>[      Icon(        Icons.add_box,        size: 24,        color: Colors.black,      ),      Positioned(        left: 10,        child: Icon(          Icons.add_circle,          size: 24,          color: Colors.black,        ),      ),    ],  );}<code_end><topic_end><topic_start>What is the equivalent of a ScrollView?In Xamarin.Forms, a ScrollView wraps around a VisualElement,and if the content is larger than the device screen, it scrolls.In Flutter, the closest match is the SingleChildScrollView widget.You simply fill the Widget with the content that you want to be scrollable.<code_start>@overrideWidget build(BuildContext context) {  return const SingleChildScrollView(    child: Text('Long Content'),  );}<code_end>If you have many items you want to wrap in a scroll,even of different Widget types, you might want to use a ListView.This might seem like overkill, but in Flutter this isfar more optimized and less intensive than a Xamarin.Forms ListView,which is backing on to platform specific controls.<code_start>@overrideWidget build(BuildContext context) {  return ListView(    children: const <Widget>[      Text('Row One'),      Text('Row Two'),      Text('Row Three'),      Text('Row Four'),    ],  );}<code_end><topic_end><topic_start>How do I handle landscape transitions in Flutter?Landscape transitions can be handled automatically by setting theconfigChanges property in the AndroidManifest.xml:<topic_end><topic_start>Gesture detection and touch event handling<topic_end><topic_start>How do I add GestureRecognizers to a widget in Flutter?In Xamarin.Forms, Elements might contain a click event you can attach to.Many elements also contain a Command that is tied to this event.Alternatively you would use the TapGestureRecognizer.In Flutter there are two very similar ways:If the widget supports event detection, pass a function to it andhandle it in the function. For example, the ElevatedButton has anonPressed parameter:<code_start>@overrideWidget build(BuildContext context) {  return ElevatedButton(    onPressed: () {      developer.log('click');    },    child: const Text('Button'),  );}<code_end>If the widget doesn’t support event detection, wrap thewidget in a GestureDetector and pass a functionto the onTap parameter.<code_start>class SampleApp extends StatelessWidget {  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      body: Center(        child: GestureDetector(          onTap: () {            developer.log('tap');          },          child: const FlutterLogo(size: 200),        ),      ),    );  }}<code_end><topic_end><topic_start>How do I handle other gestures on widgets?In Xamarin.Forms you would add a GestureRecognizer to the View.You would normally be limited to TapGestureRecognizer,PinchGestureRecognizer, PanGestureRecognizer, SwipeGestureRecognizer,DragGestureRecognizer and DropGestureRecognizer unless you built your own.In Flutter, using the GestureDetector,you can listen to a wide range of Gestures such as:The following example shows a GestureDetectorthat rotates the Flutter logo on a double tap:<code_start>class RotatingFlutterDetector extends StatefulWidget {  const RotatingFlutterDetector({super.key});  @override  State<RotatingFlutterDetector> createState() =>      _RotatingFlutterDetectorState();}class _RotatingFlutterDetectorState extends State<RotatingFlutterDetector>    with SingleTickerProviderStateMixin {  late final AnimationController controller;  late final CurvedAnimation curve;  @override  void initState() {    super.initState();    controller = AnimationController(      duration: const Duration(milliseconds: 2000),      vsync: this,    );    curve = CurvedAnimation(parent: controller, curve: Curves.easeIn);  }  @override  Widget build(BuildContext context) {    return Scaffold(      body: Center(        child: GestureDetector(          onDoubleTap: () {            if (controller.isCompleted) {              controller.reverse();            } else {              controller.forward();            }          },          child: RotationTransition(            turns: curve,            child: const FlutterLogo(size: 200),          ),        ),      ),    );  }}<code_end><topic_end><topic_start>Listviews and adapters<topic_end><topic_start>What is the equivalent to a ListView in Flutter?The equivalent to a ListView in Flutter is … a ListView!In a Xamarin.Forms ListView, you create a ViewCelland possibly a DataTemplateSelectorand pass it into the ListView,which renders each row with what yourDataTemplateSelector or ViewCell returns.However, you often have to make sure you turn on Cell Recyclingotherwise you will run into memory issues and slow scrolling speeds.Due to Flutter’s immutable widget pattern,you pass a list of widgets to your ListView,and Flutter takes care of making sure that scrolling is fast and smooth.<code_start>import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  /// This widget is the root of your application.  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatelessWidget {  const SampleAppPage({super.key});  List<Widget> _getListData() {    return List<Widget>.generate(      100,      (index) => Padding(        padding: const EdgeInsets.all(10),        child: Text('Row $index'),      ),    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Sample App')),      body: ListView(children: _getListData()),    );  }}<code_end><topic_end><topic_start>How do I know which list item has been clicked?In Xamarin.Forms, the ListView has an ItemTapped methodto find out which item was clicked.There are many other techniques you might have usedsuch as checking when SelectedItem or EventToCommandbehaviors change.In Flutter, use the touch handling provided by the passed-in widgets.<code_start>import 'dart:developer' as developer;import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  // This widget is the root of your application.  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List<Widget> _getListData() {    return List<Widget>.generate(      100,      (index) => GestureDetector(        onTap: () {          developer.log('Row $index tapped');        },        child: Padding(          padding: const EdgeInsets.all(10),          child: Text('Row $index'),        ),      ),    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Sample App')),      body: ListView(children: _getListData()),    );  }}<code_end><topic_end><topic_start>How do I update a ListView dynamically?In Xamarin.Forms, if you bound theItemsSource property to an ObservableCollection,you would just update the list in your ViewModel.Alternatively, you could assign a new List to the ItemSource property.In Flutter, things work a little differently.If you update the list of widgets inside a setState() method,you would quickly see that your data did not change visually.This is because when setState() is called,the Flutter rendering engine looks at the widget treeto see if anything has changed.When it gets to your ListView, it performs a == check,and determines that the two ListViews are the same.Nothing has changed, so no update is required.For a simple way to update your ListView,create a new List inside of setState(),and copy the data from the old list to the new list.While this approach is simple, it is not recommended for large data sets,as shown in the next example.<code_start>import 'dart:developer' as developer;import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  /// This widget is the root of your application.  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List<Widget> widgets = <Widget>[];  @override  void initState() {    super.initState();    for (int i = 0; i < 100; i++) {      widgets.add(getRow(i));    }  }  Widget getRow(int index) {    return GestureDetector(      onTap: () {        setState(() {          widgets = List<Widget>.from(widgets);          widgets.add(getRow(widgets.length));          developer.log('Row $index');        });      },      child: Padding(        padding: const EdgeInsets.all(10),        child: Text('Row $index'),      ),    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Sample App')),      body: ListView(children: widgets),    );  }}<code_end>The recommended, efficient, and effective way to build a listuses a ListView.Builder.This method is great when you have a dynamic listor a list with very large amounts of data.This is essentially the equivalent of RecyclerView on Android,which automatically recycles list elements for you:<code_start>import 'dart:developer' as developer;import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  /// This widget is the root of your application.  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  List<Widget> widgets = [];  @override  void initState() {    super.initState();    for (int i = 0; i < 100; i++) {      widgets.add(getRow(i));    }  }  Widget getRow(int index) {    return GestureDetector(      onTap: () {        setState(() {          widgets.add(getRow(widgets.length));          developer.log('Row $index');        });      },      child: Padding(        padding: const EdgeInsets.all(10),        child: Text('Row $index'),      ),    );  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Sample App')),      body: ListView.builder(        itemCount: widgets.length,        itemBuilder: (context, index) {          return getRow(index);        },      ),    );  }}<code_end>Instead of creating a ListView, create a ListView.builderthat takes two key parameters: the initial length of the list,and an item builder function.The item builder function is similar to the getView functionin an Android adapter; it takes a position,and returns the row you want rendered at that position.Finally, but most importantly, notice that the onTap() functiondoesn’t recreate the list anymore, but instead adds to it.For more information, seeYour first Flutter app codelab.<topic_end><topic_start>Working with text<topic_end><topic_start>How do I set custom fonts on my text widgets?In Xamarin.Forms, you would have to add a custom font in each native project.Then, in your Element you would assign this font nameto the FontFamily attribute using filename#fontnameand just fontname for iOS.In Flutter, place the font file in a folder and reference itin the pubspec.yaml file, similar to how you import images.Then assign the font to your Text widget:<code_start>@overrideWidget build(BuildContext context) {  return Scaffold(    appBar: AppBar(title: const Text('Sample App')),    body: const Center(      child: Text(        'This is a custom font text',        style: TextStyle(fontFamily: 'MyCustomFont'),      ),    ),  );}<code_end><topic_end><topic_start>How do I style my text widgets?Along with fonts, you can customize other styling elements on a Text widget.The style parameter of a Text widget takes a TextStyle object,where you can customize many parameters, such as:<topic_end><topic_start>Form input<topic_end><topic_start>How do I retrieve user input?Xamarin.Forms elements allow you to directly query the elementto determine the state of its properties,or whether it’s bound to a property in a ViewModel.Retrieving information in Flutter is handled by specialized widgetsand is different from how you are used to.If you have a TextFieldor a TextFormField,you can supply a TextEditingControllerto retrieve user input:<code_start>import 'package:flutter/material.dart';class MyForm extends StatefulWidget {  const MyForm({super.key});  @override  State<MyForm> createState() => _MyFormState();}class _MyFormState extends State<MyForm> {  /// Create a text controller and use it to retrieve the current value  /// of the TextField.  final TextEditingController myController = TextEditingController();  @override  void dispose() {    // Clean up the controller when disposing of the widget.    myController.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Retrieve Text Input')),      body: Padding(        padding: const EdgeInsets.all(16),        child: TextField(controller: myController),      ),      floatingActionButton: FloatingActionButton(        // When the user presses the button, show an alert dialog with the        // text that the user has typed into our text field.        onPressed: () {          showDialog(            context: context,            builder: (context) {              return AlertDialog(                // Retrieve the text that the user has entered using the                // TextEditingController.                content: Text(myController.text),              );            },          );        },        tooltip: 'Show me the value!',        child: const Icon(Icons.text_fields),      ),    );  }}<code_end>You can find more information and the full code listing inRetrieve the value of a text field,from the Flutter cookbook.<topic_end><topic_start>What is the equivalent of a Placeholder on an Entry?In Xamarin.Forms, some Elements support a Placeholder propertythat you can assign a value to. For example:In Flutter, you can easily show a “hint” or a placeholder textfor your input by adding an InputDecoration objectto the decoration constructor parameter for the text widget.<code_start>TextField(  decoration: InputDecoration(hintText: 'This is a hint'),),<code_end><topic_end><topic_start>How do I show validation errors?With Xamarin.Forms, if you wished to provide a visual hint of avalidation error, you would need to create new properties andVisualElements surrounding the Elements that had validation errors.In Flutter, you pass through an InputDecoration object to thedecoration constructor for the text widget.However, you don’t want to start off by showing an error.Instead, when the user has entered invalid data,update the state, and pass a new InputDecoration object.<code_start>import 'package:flutter/material.dart';void main() {  runApp(const SampleApp());}class SampleApp extends StatelessWidget {  /// This widget is the root of your application.  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Sample App',      home: SampleAppPage(),    );  }}class SampleAppPage extends StatefulWidget {  const SampleAppPage({super.key});  @override  State<SampleAppPage> createState() => _SampleAppPageState();}class _SampleAppPageState extends State<SampleAppPage> {  String? _errorText;  String? _getErrorText() {    return _errorText;  }  bool isEmail(String em) {    const String emailRegexp =        r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|'        r'(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|'        r'(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';    final RegExp regExp = RegExp(emailRegexp);    return regExp.hasMatch(em);  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Sample App')),      body: Center(        child: TextField(          onSubmitted: (text) {            setState(() {              if (!isEmail(text)) {                _errorText = 'Error: This is not an email';              } else {                _errorText = null;              }            });          },          decoration: InputDecoration(            hintText: 'This is a hint',            errorText: _getErrorText(),          ),        ),      ),    );  }}<code_end><topic_end><topic_start>Flutter plugins<topic_end><topic_start>Interacting with hardware, third party services, and the platform<topic_end><topic_start>How do I interact with the platform, and with platform native code?Flutter doesn’t run code directly on the underlying platform;rather, the Dart code that makes up a Flutter app is run nativelyon the device, “sidestepping” the SDK provided by the platform.That means, for example, when you perform a network request in Dart,it runs directly in the Dart context.You don’t use the Android or iOS APIsyou normally take advantage of when writing native apps.Your Flutter app is still hosted in a native app’sViewController or Activity as a view,but you don’t have direct access to this, or the native framework.This doesn’t mean Flutter apps can’t interact with those native APIs,or with any native code you have. Flutter provides platform channelsthat communicate and exchange data with theViewController or Activity that hosts your Flutter view.Platform channels are essentially an asynchronous messaging mechanismthat bridges the Dart code with the host ViewControlleror Activity and the iOS or Android framework it runs on.You can use platform channels to execute a method on the native side,or to retrieve some data from the device’s sensors, for example.In addition to directly using platform channels,you can use a variety of pre-made pluginsthat encapsulate the native and Dart code for a specific goal.For example, you can use a plugin to accessthe camera roll and the device camera directly from Flutter,without having to write your own integration.Plugins are found on pub.dev,Dart and Flutter’s open source package repository.Some packages might support native integrations on iOS,or Android, or both.If you can’t find a plugin on pub.dev that fits your needs,you can write your own, and publish it on pub.dev.<topic_end><topic_start>How do I access the GPS sensor?Use the geolocator community plugin.<topic_end><topic_start>How do I access the camera?The camera plugin is popular for accessing the camera.<topic_end><topic_start>How do I log in with Facebook?To log in with Facebook, use theflutter_facebook_login community plugin.<topic_end><topic_start>How do I use Firebase features?Most Firebase functions are covered by first party plugins.These plugins are first-party integrations, maintained by the Flutter team:You can also find some third-party Firebase plugins on pub.devthat cover areas not directly covered by the first-party plugins.<topic_end><topic_start>How do I build my own custom native integrations?If there is platform-specific functionality that Flutteror its community plugins are missing,you can build your own following thedeveloping packages and plugins page.Flutter’s plugin architecture, in a nutshell,is much like using an Event bus in Android:you fire off a message and let the receiver process and emit a resultback to you. In this case, the receiver is code running on the native sideon Android or iOS.<topic_end><topic_start>Themes (Styles)<topic_end><topic_start>How do I theme my app?Flutter comes with a beautiful, built-in implementation of Material Design,which handles much of the styling and theming needsthat you would typically do.Xamarin.Forms does have a global ResourceDictionarywhere you can share styles across your app.Alternatively, there is Theme support currently in preview.In Flutter, you declare themes in the top level widget.To take full advantage of Material Components in your app,you can declare a top level widget MaterialAppas the entry point to your application.MaterialApp is a convenience widgetthat wraps a number of widgets that are commonly requiredfor applications implementing Material Design.It builds upon a WidgetsApp by adding Material-specific functionality.You can also use a WidgetsApp as your app widget,which provides some of the same functionality,but is not as rich as MaterialApp.To customize the colors and styles of any child components,pass a ThemeData object to the MaterialApp widget.For example, in the following code,the color scheme from seed is set to deepPurple and text selection color is red.<code_start>class SampleApp extends StatelessWidget {  /// This widget is the root of your application.  const SampleApp({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Sample App',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),        textSelectionTheme:            const TextSelectionThemeData(selectionColor: Colors.red),      ),      home: const SampleAppPage(),    );  }}<code_end><topic_end><topic_start>Databases and local storage<topic_end><topic_start>How do I access shared preferences or UserDefaults?Xamarin.Forms developers will likely be familiar with theXam.Plugins.Settings plugin.In Flutter, access equivalent functionality using theshared_preferences plugin. This plugin wraps thefunctionality of both UserDefaults and the Androidequivalent, SharedPreferences.<topic_end><topic_start>How do I access SQLite in Flutter?In Xamarin.Forms most applications would use the sqlite-net-pclplugin to access SQLite databases.In Flutter, on macOS, Android, and iOS,access this functionality using thesqflite plugin.<topic_end><topic_start>Debugging<topic_end><topic_start>What tools can I use to debug my app in Flutter?Use the DevTools suite for debugging Flutter or Dart apps.DevTools includes support for profiling, examining the heap,inspecting the widget tree, logging diagnostics, debugging,observing executed lines of code,debugging memory leaks and memory fragmentation.For more information, see the DevTools documentation.<topic_end><topic_start>Notifications<topic_end><topic_start>How do I set up push notifications?In Android, you use Firebase Cloud Messaging to set uppush notifications for your app.In Flutter, access this functionality using thefirebase_messaging plugin.For more information on using the Firebase Cloud Messaging API, see thefirebase_messaging plugin documentation.<topic_end><topic_start>Introduction to declarative UIThis introduction describes the conceptual difference between thedeclarative style used by Flutter, and the imperative style used bymany other UI frameworks.<topic_end><topic_start>Why a declarative UI?Frameworks from Win32 to web to Android and iOS typically use an imperativestyle of UI programming. This might be the style you’re most familiarwith—where you manually construct a full-functioned UI entity,such as a UIView or equivalent, and later mutate it using methods andsetters when the UI changes.In order to lighten the burden on developers from having to program how totransition between various UI states, Flutter, by contrast,lets the developer describe the current UI state and leaves thetransitioning to the framework.This, however, requires a slight shift in thinking for how to manipulate UI.<topic_end><topic_start>How to change UI in a declarative frameworkConsider a simplified example below:In the imperative style, you would typically go to ViewB’s ownerand retrieve the instance b using selectors or with findViewById or similar,and invoke mutations on it (and implicitly invalidate it). For example:You might also need to replicate this configuration in the constructor ofViewB since the source of truth for the UI might outlive instance b itself.In the declarative style, view configurations (such as Flutter’s Widgets)are immutable and are only lightweight “blueprints”. To change the UI,a widget triggers a rebuild on itself (most commonly by calling setState()on StatefulWidgets in Flutter) and constructs a new Widget subtree.<code_start>// Declarative stylereturn ViewB(  color: red,  child: const ViewC(),);<code_end>Here, rather than mutating an old instance b when the UI changes,Flutter constructs new Widget instances. The framework manages many of theresponsibilities of a traditional UI object (such as maintaining thestate of the layout) behind the scenes with RenderObjects.RenderObjects persist between frames and Flutter’s lightweight Widgetstell the framework to mutate the RenderObjects between states.The Flutter framework handles the rest.<topic_end><topic_start>Flutter concurrency for Swift developersBoth Dart and Swift support concurrent programming. This guide should help you understand howconcurrency works in Dart and how it compares to Swift.With this understanding, you can createhigh-performing iOS apps.When developing in the Apple ecosystem, some tasks might take a long time to complete. These tasks include fetching or processing large amounts of data.iOS developers typically use Grand Central Dispatch (GCD)to schedule tasks using a shared thread pool.With GCD, developers add tasks to dispatch queuesand GCD decides on which thread to execute them.But, GCD spins up threads to handle remaining work items.This means you can end up with a large number of threads and the system can become over committed.With Swift, the structured concurrency model reduced the number of threads and context switches. Now, each core has only one thread.Dart has a single-threaded execution model, with support for Isolates, an event loop, and asynchronous code. An Isolate is Dart’s implementation of a lightweight thread.Unless you spawn an Isolate, your Dart code runs in the main UI thread driven by an event loop. Flutter’s event loop is equivalent to the iOS main loop—in other words, the Looper attached to the main thread.Dart’s single-threaded model doesn’t mean you are required to run everything as a blocking operation that causes the UI to freeze. Instead, use the asynchronous features that the Dart language provides, such as async/await.<topic_end><topic_start>Asynchronous ProgrammingAn asynchronous operation allows other operations to execute before it completes. Both Dart and Swift support asynchronous functions using the async and await keywords. In both cases, async marks that a function performs asynchronous work, and await tells the system to await a result from function. This means that the Dart VM could suspend the function, if necessary. For more details on asynchronous programming, check outConcurrency in Dart.<topic_end><topic_start>Leveraging the main thread/isolateFor Apple operating systems, the primary (also called the main) thread is where the application begins running. Rendering the user interface always happens on the main thread. One difference between Swift and Dart is thatSwift might use different threads for different tasks, and Swift doesn’t guarantee which thread is used. So, when dispatching UI updates in Swift, you might need to ensure that the work occurs on the main thread.Say you want to write a function that fetches the weather asynchronously and displays the results.In GCD, to manually dispatch a process to the main thread, you might do something like the following.First, define the Weather enum:Next, define the view model and mark it as an ObservableObject so that it can return a value of type Weather?. Use GCD create to a DispatchQueue to send the work to the pool of threadsFinally, display the results:More recently, Swift introduced actors to support synchronization for shared, mutable state. To ensure that work is performed on the main thread,define a view model class that is marked as a @MainActor, with a load() function that internally calls an asynchronous function using Task.Next, define the view model as a state object using @StateObject, with a load() function that can be called by the view model:In Dart, all work runs on the main isolate by default. To implement the same example in Dart, first, create the Weather enum:<code_start>enum Weather {  rainy,  windy,  sunny,}<code_end>Then, define a simple view model (similar to what was created in SwiftUI), to fetch the weather. In Dart, a Future object represents a value to beprovided in the future. A Future is similar to Swift’s ObservableObject. In this example, a function within the view modelreturns a Future<Weather> object:<code_start>@immutableclass HomePageViewModel {  const HomePageViewModel();  Future<Weather> load() async {    await Future.delayed(const Duration(seconds: 1));    return Weather.sunny;  }}<code_end>The load() function in this example shares similarities with the Swift code. The Dart function is marked as async becauseit uses the await keyword.Additionally, a Dart function marked as asyncautomatically returns a Future.In other words, you don’t have to create a Future instance manually inside functions marked as async.For the last step, display the weather value. In Flutter, FutureBuilder and StreamBuilderwidgets are used to display the results of a Future in the UI. The following example uses a FutureBuilder:<code_start>class HomePage extends StatelessWidget {  const HomePage({super.key});  final HomePageViewModel viewModel = const HomePageViewModel();  @override  Widget build(BuildContext context) {    return CupertinoPageScaffold(      // Feed a FutureBuilder to your widget tree.      child: FutureBuilder<Weather>(        // Specify the Future that you want to track.        future: viewModel.load(),        builder: (context, snapshot) {          // A snapshot is of type `AsyncSnapshot` and contains the          // state of the Future. By looking if the snapshot contains          // an error or if the data is null, you can decide what to          // show to the user.          if (snapshot.hasData) {            return Center(              child: Text(                snapshot.data.toString(),              ),            );          } else {            return const Center(              child: CupertinoActivityIndicator(),            );          }        },      ),    );  }}<code_end>For the complete example, check out theasync_weather file on GitHub.<topic_end><topic_start>Leveraging a background thread/isolateFlutter apps can run on a variety of multi-core hardware, including devices running macOS and iOS. To improve the performance of these applications, you must sometimes run tasks on different coresconcurrently. This is especially important to avoid blocking UI rendering with long-running operations.In Swift, you can leverage GCD to run tasks on global queueswith different quality of service class (qos) properties. This indicates the task’s priority.In Dart, you can offload computation to a worker isolate, often called a background worker. A common scenario spawns a simple worker isolate and returns the results in a message when the worker exits. As of Dart 2.19, you can use Isolate.run() to spawn an isolate and run computations:In Flutter, you can also use the compute function to spin up an isolate to run a callback function:In this case, the callback function is a top-levelfunction as shown below:You can find more information on Dart atLearning Dart as a Swift developer,and more information on Flutter atFlutter for SwiftUI developers orFlutter for UIKit developers.<topic_end><topic_start>Upgrading FlutterNo matter which one of the Flutter release channelsyou follow, you can use the flutter command to upgrade yourFlutter SDK or the packages that your app depends on.<topic_end><topic_start>Upgrading the Flutter SDKTo update the Flutter SDK use the flutter upgrade command:This command gets the most recent version of the Flutter SDKthat’s available on your current Flutter channel.If you are using the stable channeland want an even more recent version of the Flutter SDK,switch to the beta channel using flutter channel beta,and then run flutter upgrade.<topic_end><topic_start>Keeping informedWe publish migration guides for known breaking changes.We send announcements regarding these changes to theFlutter announcements mailing list.To avoid being broken by future versions of Flutter,consider submitting your tests to our test registry.<topic_end><topic_start>Switching Flutter channelsFlutter has two release channels:stable and beta.<topic_end><topic_start>The stable channelWe recommend the stable channel for new usersand for production app releases.The team updates this channel about every three months.The channel might receive occasional hot fixesfor high-severity or high-impact issues.The continuous integration for the Flutter team’s plugins and packagesincludes testing against the latest stable release.The latest documentation for the stable branchis at: https://api.flutter.dev<topic_end><topic_start>The beta channelThe beta channel has the latest stable release.This is the most recent version of Flutter that we have heavily tested.This channel has passed all our public testing,has been verified against test suites for Google products that use Flutter,and has been vetted against contributed private test suites.The beta channel receives regular hot fixesto address newly discovered important issues.The beta channel is essentially the same as the stable channelbut updated monthly instead of quarterly.Indeed, when the stable channel is updated,it is updated to the latest beta release.<topic_end><topic_start>Other channelsWe currently have one other channel, master.People who contribute to Flutter use this channel.This channel is not as thoroughly tested asthe beta and stable channels.We do not recommend using this channel asit is more likely to contain serious regressions.The latest documentation for the master branchis at: https://main-api.flutter.dev<topic_end><topic_start>Changing channelsTo view your current channel, use the following command:To change to another channel, use flutter channel <channel-name>.Once you’ve changed your channel, use flutter upgradeto download the latest Flutter SDK and dependent packages for that channel.For example:info Note  If you need a specific version of the Flutter SDK,  you can download it from the Flutter SDK archive.<topic_end><topic_start>Upgrading packagesIf you’ve modified your pubspec.yaml file, or you want to updateonly the packages that your app depends upon(instead of both the packages and Flutter itself),then use one of the flutter pub commands.To update to the latest compatible versions ofall the dependencies listed in the pubspec.yaml file,use the upgrade command:To update to the latest possible version ofall the dependencies listed in the pubspec.yaml file,use the upgrade --major-versions command:This also automatically update the constraintsin the pubspec.yaml file.To identify out-of-date package dependencies and get adviceon how to update them, use the outdated command. For details, seethe Dart pub outdated documentation.<topic_end><topic_start>Flutter SDK archiveThe Stable channel contains themost stable Flutter builds.To learn more, check out Flutter’s channels.error Important  If you develop apps in China, check out using Flutter in China.To learn what’s new in the major Flutter releases,check out the release notes page.A note on provenance: provenance  describes how software artifacts are built, including  what the download contains and who created it.  To view provenance in a more readable format  and where nothing is downloaded, run the following  command using the provenance file URL from a release (you might need to   download jq to easily parse the JSON).<topic_end><topic_start>Stable channel (Windows)Select from the following scrollable list:<topic_end><topic_start>Beta channel (Windows)Select from the following scrollable list:<topic_end><topic_start>Stable channel (macOS)Select from the following scrollable list:<topic_end><topic_start>Beta channel (macOS)Select from the following scrollable list:<topic_end><topic_start>Stable channel (Linux)Select from the following scrollable list:<topic_end><topic_start>Beta channel (Linux)Select from the following scrollable list:<topic_end><topic_start>Master channelInstallation bundles are not available for master.However, you can get the SDK directly fromGitHub repo by cloning the master channel,and then triggering a download of the SDK dependencies:For additional details on how our installation bundles are structured,see Installation bundles.<topic_end><topic_start>What's newThis page contains current and recent announcementsof what’s new on the Flutter website and blog.Find past what’s new information on thewhat’s new archive page.You might also check out theFlutter SDK release notes.To stay on top of Flutter announcements includingbreaking changes,join the flutter-announce Google group.For Dart, you can join the Dart Announce Google group,and review the Dart changelog.<topic_end><topic_start>15 February 2024: Valentine’s-Day-adjacent 3.19 releaseFlutter 3.19 is live! For more information,check out the Flutter 3.19 umbrella blog postand the Flutter 3.19 technical blog post.You might also check out the Dart 3.3 release blog post.Docs updated or added since the 3.16 releaseOther updatesFor past releases, check out theWhat’s new archive page.<topic_end><topic_start>Flutter release notesThis page links to announcements and release notes forreleases to the stable channel.info Note  For information about bug-fix releases, check out  Hotfixes to the Stable Channel on the Flutter wiki.<topic_end><topic_start>Breaking changes and migration guidesAs described in the breaking change policy,on occasion we publish guidesfor migrating code across a breaking change.To be notified about future breaking changes,join the groups Flutter announce and Dart announce.When facing Dart errors after upgrading Flutter,consider using the dart fix commandto automatically migrate your code.Not every breaking change is supported in this way,but many are.To avoid being broken by future versions of Flutter,consider submitting your tests to our test registry.The following guides are available. They are sorted byrelease, and listed in alphabetical order:<topic_end><topic_start>Not yet released to stable<topic_end><topic_start>Released in Flutter 3.19<topic_end><topic_start>Released in Flutter 3.16<topic_end><topic_start>Released in Flutter 3.13<topic_end><topic_start>Released in Flutter 3.10<topic_end><topic_start>Released in Flutter 3.7<topic_end><topic_start>Released in Flutter 3.3<topic_end><topic_start>Released in Flutter 3<topic_end><topic_start>Released in Flutter 2.10<topic_end><topic_start>Released in Flutter 2.5<topic_end><topic_start>Reverted change in 2.2The following breaking change was reverted in release 2.2:<topic_end><topic_start>Released in Flutter 2.2<topic_end><topic_start>Released in Flutter 2<topic_end><topic_start>Released in Flutter 1.22<topic_end><topic_start>Released in Flutter 1.20<topic_end><topic_start>Released in Flutter 1.17<topic_end><topic_start>Flutter compatibility policyThe Flutter team tries to balance the need for API stability with theneed to keep evolving APIs to fix bugs, improve API ergonomics,and provide new features in a coherent manner.To this end, we have created a test registry where you can provideunit tests for your own applications or libraries that we runon every change to help us track changes that would breakexisting applications. Our commitment is that we won’t make anychanges that break these tests without working with the developers ofthose tests to (a) determine if the change is sufficiently valuable,and (b) provide fixes for the code so that the tests continue to pass.If you would like to provide tests as part of this program, pleasesubmit a PR to the flutter/tests repository. The README on that repository describes the process in detail.<topic_end><topic_start>Announcements and migration guidesIf we do make a breaking change (defined as a change that caused oneor more of these submitted tests to require changes), we will announcethe change on our flutter-announcemailing list as well as in our release notes.We provide a list of guides for migrating code affected bybreaking changes.<topic_end><topic_start>Deprecation policyWe will, on occasion, deprecate certain APIs rather than outrightbreak them overnight. This is independent of our compatibility policywhich is exclusively based on whether submitted tests fail, asdescribed above.Deprecated APIs are removed after a migration grace period. This graceperiod is one calendar year after being released on the stable channel,or after 4 stable releases, whichever is longer.When a deprecation does reach end of life, we follow the same procedureslisted above for making breaking changes in removing the deprecated API.<topic_end><topic_start>Dart and other libraries used by FlutterThe Dart language itself has a separate breaking-change policy,with announcements on Dart announce.In general, the Flutter team doesn’t currently have any commitmentregarding breaking changes for other dependencies.For example, it’s possible that a new version ofFlutter using a new version of Skia(the graphics engine used by some platforms on Flutter)or Harfbuzz (the font shaping engine used by Flutter)would have changes that affect contributed tests.Such changes wouldn’t necessarily be accompanied by amigration guide.<topic_end><topic_start>CodelabsThe Flutter codelabs provide a guided,hands-on coding experience. Some codelabsrun in DartPad—no downloads required!<topic_end><topic_start>Good for beginnersIf you’re new to Flutter, we recommend starting withone of the following codelabs:Building your first Flutter app (workshop)An instructor-led version of our very popular“Write your first Flutter app” codelab(listed below).Your first Flutter appCreate a simple app that automatically generates cool-sounding names,such as “newstay”, “lightstream”, “mainbrake”, or “graypine”.This app is responsive and runs on mobile, desktop, and web.(This also replaces the previous “write your first Flutter app”for mobile, part 1 and part 2 codelabs.)Write your first Flutter app on the webImplement a simple web app in DartPad (no downloadsrequired!) that displays a sign-in screencontaining three text fields. As the user fills out thefields, a progress bar animates along the top of thesign-in area. This codelab is written specifically forthe web, but if you have downloaded and configuredAndroid and iOS tooling, the completed appworks on Android and iOS devices, as well.<topic_end><topic_start>Next stepsRecords and Patterns in Dart 3Discover Dart 3’s new records and patterns features.Learn how you can use them in a Flutter app to help youwrite more readable and maintainable Dart code.Building scrolling experiences in Flutter (workshop)Start with an app that performs simple, straightforward scrollingand enhance it to create fancy and custom scrolling effectsby using slivers.Dart null safety in Action (workshop)An instructor-led workshop introducing the featuresthat enable Dart’s null-safe type system.How to manage application states using inherited widgets (workshop)Learn how to manage the state of your app’s data byusing the InheritedWidget class, one of thelow-level state management classes providedby Flutter.<topic_end><topic_start>Designing a Flutter UILearn about Material Design and basic Flutter concepts,like layout and animations:How to debug layout issues with the Flutter InspectorNot an official codelab, but step-by-step instructions onhow to debug common layout problems using the FlutterInspector and Layout Explorer.Implicit animationsUse DartPad (no downloads required!) to learn how to useimplicit animations to add motion and createvisual effects for the widgets in your UI.Building Beautiful Transitions with Material Motion for FlutterLearn how to use the Material animations package toadd pre-built transitions to a Material app called Reply.Take your Flutter app from boring to beautifulLearn how to use some of the features in Material 3to make your app more beautiful and more responsive.MDC-101 Flutter: Material Components (MDC) BasicsLearn the basics of using Material Components by buildinga simple app with core components.  The four MDC codelabsguide you through building an e-commerce app called Shrine.You’ll start by building a login page using several of MDCFlutter’s components.MDC-102 Flutter: Material Structure and LayoutLearn how to use Material for structure and layout in Flutter.Continue building the e-commerce app, introduced in MDC-101,by adding navigation, structure, and data.MDC-103 Flutter: Material Theming with Color, Shape, Elevation, and TypeDiscover how Material Components for Flutter make iteasy to differentiate your product, and express yourbrand through design. Continue building your e-commerceapp by adding a home screen that displays products.MDC-104 Flutter: Material Advanced ComponentsImprove your design and learn to use our advancedcomponent backdrop menu. Finish your e-commerce appby adding a backdrop with a menu that filtersproducts by the selected category.Adaptive Apps in FlutterLearn how to build a Flutter app that adapts to theplatform that it’s running on, be that Android, iOS,the web, Windows, macOS, or Linux.Building next generation UIs in FlutterLearn how to build a Flutter app that uses the power of flutter_animate,fragment shaders, and particle fields. You will craft a user interface thatevokes those science fiction movies and TV shows we all lovewatching when we aren’t coding.<topic_end><topic_start>Using Flutter withLearn how to use Flutter with other technologies.<topic_end><topic_start>Monetizing FlutterAdding AdMob Ads to a Flutter appLearn how to add an AdMob banner, an interstitial ad,and a rewarded ad to an app called Awesome Drawing Quiz,a game that lets players guess the name of the drawing.Adding an AdMob banner and native inline ads to a Flutter appLearn how to implement inline banner and native adsto a travel booking app that lists possibleflight destinations.Adding in-app purchases to your Flutter appExtend a simple gaming app that uses the Dash mascot ascurrency to offer three types of in-app purchases:consumable, non-consumable, and subscription.<topic_end><topic_start>Flutter and FirebaseAdd a user authentication flow to a Flutter app using FirebaseUILearn how to add Firebase authentication to a Flutter appwith only a few lines of code.Get to know Firebase for Flutter (workshop)An instructor-led version of our popular“Get to know Firebase for Flutter” codelab(listed below).Get to know Firebase for FlutterBuild an event RSVP and guestbook chat app on both Androidand iOS using Flutter, authenticating users with FirebaseAuthentication, and sync data using Cloud Firestore.Local development for your Flutter apps using the Firebase Emulator SuiteLearn how to use the Firebase Emulator Suite whendeveloping with Flutter. You will also learn to usethe Auth and Firestore emulators.<topic_end><topic_start>Flutter and TensorFlowCreate a custom text-classification model with TensorFlow Lite Model MakerCreate a Flutter app to classify texts with TensorFlowLearn how to run a text-classification inference from a Flutterapp with TensorFlow Serving through REST and gRPC.Train a comment-spam detection model with TensorFlow Lite Model MakerLearn how to install the TensorFlow Lite Model Maker with Colab,how to use a data loader, and how to build a model.<topic_end><topic_start>Flutter and other technologiesAdding Google Maps to a Flutter appDisplay a Google map in an app, retrieve data from aweb service, and display the data as markers on the map.Adding WebView to your Flutter appWith the WebView Flutter plugin you can add a WebViewwidget to your Android or iOS Flutter app.Build voice bots for mobile with Dialogflow and Flutter (workshop)An instructor-led version of the Dialogflowand Flutter codelab (listed below).Build voice bots for Android with Dialogflow and FlutterLearn how to build a mobile FAQ bot that can answer mostcommon questions about the tool Dialogflow. End userscan interact with the text interface or stream a voiceinteraction via the built-in microphone of a mobile device.Introduction to Flame with FlutterBuild a Breakout clone using the Flame 2D game engine andembed it in a Flutter wrapper. You will use Flame’s Effectsto animate and remove components, along with the google_fonts andflutter_animate packages, to make the whole game look well designed.Using FFI in a Flutter pluginLearn how to use Dart’s FFI (foreign function interface)library, ffigen, allowing you to leverageexisting native libraries that provide aC interface.Create haikus about Google products with the PaLM API and FlutterLearn how to build an app that uses the PaLM API togenerate haikus based on Google product names. ThePaLM API gives you access to Google’sstate-of-the-art large language models.<topic_end><topic_start>TestingLearn how to test your Flutter application.<topic_end><topic_start>Writing platform-specific codeLearn how to write code that’s targeted for specific platforms,like iOS, Android, desktop, or the web.How to write a Flutter pluginLearn how to write a plugin by creating a music pluginfor iOS and Android that processes audio on the host platform.Then make an example app that uses your plugin to make a music keyboard.Using a plugin with a Flutter web appFinish an app that reports the number of stars on a GitHub repository.Use Dart DevTools to do some simple debugging, andhost your app on Firebase and, finally, use a Flutter plugin tolaunch the app and open the hosted privacy policy.Write a Flutter desktop applicationBuild a Flutter desktop app (Windows, Linux, or macOS)that accesses GitHub APIs to retrieve your repositories,assigned issues, and pull requests. As part of this task,create and use plugins to interact with native APIs and desktop applications,and use code generation to build type-safe client libraries for GitHub’s APIs.Adding a Home Screen widget to your Flutter app NEWLearn how to add a Home Screen widget to your Flutter appon iOS. This applies to your home screen, lock screen, or thetoday view.<topic_end><topic_start>Other resourcesFor Dart-specific codelabs, see thecodelabs page on the Dart site.We also recommend the following online class:info Note  If you have trouble viewing any of the codelabs  on codelabs.developers.google.com, try  this mirror of the Flutter codelabs.<topic_end><topic_start>CookbookThis cookbook contains recipes that demonstrate how to solve common problems while writing Flutter apps. Each recipe is self-contained and can be used as areference to help you build up an application.<topic_end><topic_start>Animation<topic_end><topic_start>Design<topic_end><topic_start>Effects<topic_end><topic_start>Forms<topic_end><topic_start>Games<topic_end><topic_start>Gestures<topic_end><topic_start>Images<topic_end><topic_start>Lists<topic_end><topic_start>Maintenance<topic_end><topic_start>Navigation<topic_end><topic_start>Networking<topic_end><topic_start>Persistence<topic_end><topic_start>Plugins<topic_end><topic_start>Testing<topic_end><topic_start>Integration<topic_end><topic_start>Unit<topic_end><topic_start>Widget<topic_end><topic_start>Casual Games ToolkitThe Flutter Casual Games Toolkit pulls together new and existing resourcesso you can accelerate development of games on mobile platforms.This page outlines where you can find these available resources.<topic_end><topic_start>Why Flutter for games?The Flutter framework can create performant apps for six target platformsfrom the desktop to mobile devices to the web.With Flutter’s benefits of cross-platform development, performance, andopen source licensing, it makes a great choice for games.Casual games fall into two categories: turn-based gamesand real-time games.You might be familiar with both types of games,though perhaps you didn’t think about them in quite this way.Turn-based games cover games meant for a mass market withsimple rules and gameplay.This includes board games, card games, puzzles, and strategy games.These games respond to simple user input,like tapping on a card or entering a number or letter.These games are well suited for Flutter.Real-time games cover games a series of actions require real time responses.These include endless runner games, racing games, and so on.You might want to create a game with advanced features like collision detection,camera views, game loops, and the like.These types of games could use an open source game engine like theFlame game engine built using Flutter.<topic_end><topic_start>What’s included in the toolkitThe Casual Games Toolkit provides the following free resources.A repository that includes three new game templates that providea starting point for building a casual game.A base game templatethat includes the basics for:A card game templatethat includes everything in the base template plus:An endless runner template created in partnershipwith the open source game engine, Flame. It implements:A sample game built on top of the endless runner template,called SuperDash. You can play the game on iOS, Android,or web, view the open source code repo, or read how the game was created in 6 weeks.The included game templates and cookbook recipes make certain choicesto accelerate development.They include specific packages, like provider, google_mobile_ads,in_app_purchase, audioplayers, crashlytics, and games_services.If you prefer other packages, you can change the code to use them.The Flutter team understands that monetization might be a future consideration.Cookbook recipes for advertising and in-app purchases have been added.As explained on the Games page,you can leverage up to $900 in offers when you integrate Google services,such as Cloud, Firebase, and Ads, into your game.error ImportantTerms and conditions apply.  You must connect your Firebase and GCP accounts to use credits for  Firebase services and verify your business email during sign up to earn  an additional $100 on top of the normal $300 credit.  For the Ads offer, check your region’s eligibility.<topic_end><topic_start>Get startedAre you ready? To get started:Review the README file for the first type of game you want to create.Review the codelabs and cookbook recipes.<topic_end><topic_start>Example gamesFor Google I/O 2022, both the Flutter team and Very Good Ventures created newgames.VGV created the I/O Pinball game using the Flame engine.To learn about this game,check out I/O Pinball Powered by Flutter and Firebase on Mediumand play the game in your browser.The Flutter team created I/O Flip, a virtual CCG.To learn more about I/O Flip,check out How It’s Made: I/O FLIP adds a twist to a classic card game with generative AIon the Google Developers blog and play the game in your browser.<topic_end><topic_start>Other resourcesOnce you feel ready to go beyond these games templates,investigate other resources that our community recommended.package_2 Flutter packageapi API documentationscience Codelab book_5 Cookbook recipehandyman Desktop applicationphoto_album Game assetsquick_reference_all Guidebook_5 Special effectshandyman Spriter Propackage_2 rivepackage_2 spriteWidgetpackage_2 app_reviewpackage_2 audioplayersscience User Authentication using Firebasescience Add Firebase to your Flutter gamequick_reference_all Firebase Crashlytics overviewpackage_2 firebase_crashlyticspackage_2 win32_gamepadphoto_album CraftPixphoto_album Game Developer Studiohandyman GIMPpackage_2 Flamepackage_2 Bonfirepackage_2 forge2dbook_5 Add achievements and leaderboards to your gamebook_5 Add multiplayer support to your gamepackage_2 games_servicesscience Use the Foreign Function Interface in a Flutter pluginhandyman Tiledbook_5 Add advertising to your Flutter gamescience Add AdMob ads to a Flutter appscience Add in-app purchases to your Flutter appquick_reference_all Gaming UX and Revenue Optimizations for Apps (PDF)package_2 shared_preferencespackage_2 sqflitepackage_2 cbl_flutter (Couchbase Lite)api Paint APIbook_5 Special effectsscience Build next generation UIs in Flutter<topic_end><topic_start>Add achievements and leaderboards to your mobile gameGamers have various motivations for playing games.In broad strokes, there are four major motivations: immersion, achievement, cooperation, and competition.No matter the game you build, some players want to achieve in it.This could be trophies won or secrets unlocked.Some players want to compete in it.This could be hitting high scores or accomplishing speedruns.These two ideas map to the concepts of achievements and leaderboards.Ecosystems such as the App Store and Google Play providecentralized services for achievements and leaderboards.Players can view achievements from all their games in one place anddevelopers don’t need to re-implement them for every game.This recipe demonstrates how to use the games_services package to add achievements and leaderboard functionality to your mobile game.<topic_end><topic_start>1. Enable platform servicesTo enable games services, set up Game Center on iOS andGoogle Play Games Services on Android.<topic_end><topic_start>iOSTo enable Game Center (GameKit) on iOS:Open your Flutter project in Xcode.Open ios/Runner.xcworkspaceSelect the root Runner project.Go to the Signing & Capabilities tab.Click the + button to add Game Center as a capability.Close Xcode.If you haven’t already,register your game in App Store Connectand from the My App section press the + icon.Still in App Store Connect, look for the Game Center section. Youcan find it in Services as of this writing. On the GameCenter page, you might want to set up a leaderboard and severalachievements, depending on your game. Take note of the IDs of theleaderboards and achievements you create.<topic_end><topic_start>AndroidTo enable Play Games Services on Android:If you haven’t already, go to Google Play Consoleand register your game there.Still in Google Play Console, select Play Games Services → Setupand management → Configuration from the navigation menu andfollow their instructions.This takes a significant amount of time and patience.Among other things, you’ll need to set up anOAuth consent screen in Google Cloud Console.If at any point you feel lost, consult theofficial Play Games Services guide.When done, you can start adding leaderboards and achievements inPlay Games Services → Setup and management. Create the exactsame set as you did on the iOS side. Make note of IDs.Go to Play Games Services → Setup and management → Publishing.Click Publish. Don’t worry, this doesn’t actually publish yourgame. It only publishes the achievements and leaderboard. Once aleaderboard, for example, is published this way, it cannot beunpublished.Go to Play Games Services → Setup and management →Configuration → Credentials.Find the Get resources button.It returns an XML file with the Play Games Services IDs.Add a file at android/app/src/main/res/values/games-ids.xmlcontaining the XML you received in the previous step.<topic_end><topic_start>2. Sign in to the game serviceNow that you have set up Game Center and Play Games Services, andhave your achievement & leaderboard IDs ready, it’s finally Dart time.Add a dependency on the games_services package.Before you can do anything else, you have to sign the player intothe game service.<code_start>try {  await GamesServices.signIn();} on PlatformException catch (e) {  // ... deal with failures ...}<code_end>The sign in happens in the background. It takes several seconds, sodon’t call signIn() before runApp() or the players will be forced tostare at a blank screen every time they start your game.The API calls to the games_services API can fail for a multitude ofreasons. Therefore, every call should be wrapped in a try-catch block asin the previous example. The rest of this recipe omits exceptionhandling for clarity.lightbulb Tip  It’s a good practice to create a controller. This would be a  ChangeNotifier, a bloc, or some other piece of logic that wraps around  the raw functionality of the games_services plugin.<topic_end><topic_start>3. Unlock achievementsRegister achievements in Google Play Console and App Store Connect,and take note of their IDs. Now you can award any of thoseachievements from your Dart code:<code_start>await GamesServices.unlock(  achievement: Achievement(    androidID: 'your android id',    iOSID: 'your ios id',  ),);<code_end>The player’s account on Google Play Games or Apple Game Center nowlists the achievement.To display the achievements UI from your game, call thegames_services API:<code_start>await GamesServices.showAchievements();<code_end>This displays the platform achievements UI as an overlay on your game.To display the achievements in your own UI, useGamesServices.loadAchievements().<topic_end><topic_start>4. Submit scoresWhen the player finishes a play-through, your game can submit the resultof that play session into one or more leaderboards.For example, a platformer game like Super Mario can submit both thefinal score and the time taken to complete the level, to two separateleaderboards.In the first step, you registered a leaderboard in Google PlayConsole and App Store Connect, and took note of its ID. Using thisID, you can submit new scores for the player:<code_start>await GamesServices.submitScore(  score: Score(    iOSLeaderboardID: 'some_id_from_app_store',    androidLeaderboardID: 'sOmE_iD_fRoM_gPlAy',    value: 100,  ),);<code_end>You don’t need to check whether the new score is the player’shighest. The platform game services handle that for you.To display the leaderboard as an overlay over your game, make thefollowing call:<code_start>await GamesServices.showLeaderboards(  iOSLeaderboardID: 'some_id_from_app_store',  androidLeaderboardID: 'sOmE_iD_fRoM_gPlAy',);<code_end>If you want to display the leaderboard scores in your own UI, youcan fetch them with GamesServices.loadLeaderboardScores().<topic_end><topic_start>5. Next stepsThere’s more to the games_services plugin. With this plugin, you can:Some achievements can be incremental. For example: “You have collectedall 10 pieces of the McGuffin.”Each game has different needs from game services.To start, you might want to create this controller in order to keep all achievements & leaderboards logic in one place:<code_start>import 'dart:async';import 'package:games_services/games_services.dart';import 'package:logging/logging.dart';/// Allows awarding achievements and leaderboard scores,/// and also showing the platforms' UI overlays for achievements/// and leaderboards.////// A facade of `package:games_services`.class GamesServicesController {  static final Logger _log = Logger('GamesServicesController');  final Completer<bool> _signedInCompleter = Completer();  Future<bool> get signedIn => _signedInCompleter.future;  /// Unlocks an achievement on Game Center / Play Games.  ///  /// You must provide the achievement ids via the [iOS] and [android]  /// parameters.  ///  /// Does nothing when the game isn't signed into the underlying  /// games service.  Future<void> awardAchievement(      {required String iOS, required String android}) async {    if (!await signedIn) {      _log.warning('Trying to award achievement when not logged in.');      return;    }    try {      await GamesServices.unlock(        achievement: Achievement(          androidID: android,          iOSID: iOS,        ),      );    } catch (e) {      _log.severe('Cannot award achievement: $e');    }  }  /// Signs into the underlying games service.  Future<void> initialize() async {    try {      await GamesServices.signIn();      // The API is unclear so we're checking to be sure. The above call      // returns a String, not a boolean, and there's no documentation      // as to whether every non-error result means we're safely signed in.      final signedIn = await GamesServices.isSignedIn;      _signedInCompleter.complete(signedIn);    } catch (e) {      _log.severe('Cannot log into GamesServices: $e');      _signedInCompleter.complete(false);    }  }  /// Launches the platform's UI overlay with achievements.  Future<void> showAchievements() async {    if (!await signedIn) {      _log.severe('Trying to show achievements when not logged in.');      return;    }    try {      await GamesServices.showAchievements();    } catch (e) {      _log.severe('Cannot show achievements: $e');    }  }  /// Launches the platform's UI overlay with leaderboard(s).  Future<void> showLeaderboard() async {    if (!await signedIn) {      _log.severe('Trying to show leaderboard when not logged in.');      return;    }    try {      await GamesServices.showLeaderboards(        // TODO: When ready, change both these leaderboard IDs.        iOSLeaderboardID: 'some_id_from_app_store',        androidLeaderboardID: 'sOmE_iD_fRoM_gPlAy',      );    } catch (e) {      _log.severe('Cannot show leaderboard: $e');    }  }  /// Submits [score] to the leaderboard.  Future<void> submitLeaderboardScore(int score) async {    if (!await signedIn) {      _log.warning('Trying to submit leaderboard when not logged in.');      return;    }    _log.info('Submitting $score to leaderboard.');    try {      await GamesServices.submitScore(        score: Score(          // TODO: When ready, change these leaderboard IDs.          iOSLeaderboardID: 'some_id_from_app_store',          androidLeaderboardID: 'sOmE_iD_fRoM_gPlAy',          value: score,        ),      );    } catch (e) {      _log.severe('Cannot submit leaderboard score: $e');    }  }}<code_end><topic_end><topic_start>More informationThe Flutter Casual Games Toolkit includes the following templates:<topic_end><topic_start>Add ads to your mobile Flutter app or gameMany developers use advertising to monetize their mobile apps and games.This allows their app to be downloaded free of charge, which improves the app’s popularity.To add ads to your Flutter project, useAdMob, Google’s mobile advertising platform.This recipe demonstrates how to use thegoogle_mobile_adspackage to add a banner ad to your app or game.info Note  Apart from AdMob, the google_mobile_ads package also supports  Ad Manager, a platform intended for large publishers. Integrating Ad  Manager resembles integrating AdMob, but it won’t be covered in this  cookbook recipe. To use Ad Manager, follow the  Ad Manager documentation.<topic_end><topic_start>1. Get AdMob App IDsGo to AdMob and set up anaccount. This could take some time because you need to providebanking information, sign contracts, and so on.With the AdMob account ready, create two Apps in AdMob: one forAndroid and one for iOS.Open the App settings section.Get the AdMob App IDs for both the Android app and the iOS app.They resemble ca-app-pub-1234567890123456~1234567890. Note thetilde (~) between the two numbers.<topic_end><topic_start>2. Platform-specific setupUpdate your Android and iOS configurations to include your App IDs.<topic_end><topic_start>AndroidAdd your AdMob app ID to your Android app.Open the app’s android/app/src/main/AndroidManifest.xml file.Add a new <meta-data> tag.Set the android:name element with a value ofcom.google.android.gms.ads.APPLICATION_ID.Set the android:value element with the value to your own AdMob appID that you got in the previous step. Include them in quotes as shown:<topic_end><topic_start>iOSAdd your AdMob app ID to your iOS app.Open your app’s ios/Runner/Info.plist file.Enclose GADApplicationIdentifier with a key tag.Enclose your AdMob app ID with a string tag. You created this AdMobApp ID in step 1.<topic_end><topic_start>3. Add the google_mobile_ads pluginTo add the google_mobile_ads plugin as a dependency, runflutter pub add:info Note  Once you add the plugin, your Android app might fail to build with a  DexArchiveMergerException:To resolve this, execute the flutter run command in the terminal, not  through an IDE plugin. The flutter tool can detect the issue and ask  whether it should try to solve it. Answer y, and the problem goes away.  You can return to running your app from an IDE after that.<topic_end><topic_start>4. Initialize the Mobile Ads SDKYou need to initialize the Mobile Ads SDK before loading ads.Call MobileAds.instance.initialize() to initialize the Mobile AdsSDK.<code_start>void main() async {  WidgetsFlutterBinding.ensureInitialized();  unawaited(MobileAds.instance.initialize());  runApp(MyApp());}<code_end>Run the initialization step at startup, as shown above, so that the AdMob SDK has enough time to initialize before it is needed.info NoteMobileAds.instance.initialize() returns a Future but, the  way the SDK is built, you don’t need to await it.  If you try to load an ad before that Future is completed,   the SDK will gracefully wait until the initialization, and then load the ad.  You can await the Future  if you want to know the exact time when the AdMob SDK is ready.<topic_end><topic_start>5. Load a banner adTo show an ad, you need to request it from AdMob.To load a banner ad, construct a BannerAd instance, andcall load() on it.info Note  The following code snippet refers to fields such a adSize, adUnitId  and _bannerAd. This will all make more sense in a later step.<code_start>/// Loads a banner ad.void _loadAd() {  final bannerAd = BannerAd(    size: widget.adSize,    adUnitId: widget.adUnitId,    request: const AdRequest(),    listener: BannerAdListener(      // Called when an ad is successfully received.      onAdLoaded: (ad) {        if (!mounted) {          ad.dispose();          return;        }        setState(() {          _bannerAd = ad as BannerAd;        });      },      // Called when an ad request failed.      onAdFailedToLoad: (ad, error) {        debugPrint('BannerAd failed to load: $error');        ad.dispose();      },    ),  );  // Start loading.  bannerAd.load();}<code_end>To view a complete example, check out the last step of this recipe.<topic_end><topic_start>6. Show banner adOnce you have a loaded instance of BannerAd, use AdWidget to show it.It’s a good idea to wrap the widget in a SafeArea (so that no part ofthe ad is obstructed by device notches) and a SizedBox (so that it hasits specified, constant size before and after loading).<code_start>@overrideWidget build(BuildContext context) {  return SafeArea(    child: SizedBox(      width: widget.adSize.width.toDouble(),      height: widget.adSize.height.toDouble(),      child: _bannerAd == null          // Nothing to render yet.          ? SizedBox()          // The actual ad.          : AdWidget(ad: _bannerAd!),    ),  );}<code_end>You must dispose of an ad when you no longer need to access it. The bestpractice for when to call dispose() is either after the AdWidget isremoved from the widget tree or in theBannerAdListener.onAdFailedToLoad() callback.<code_start>_bannerAd?.dispose();<code_end><topic_end><topic_start>7. Configure adsTo show anything beyond test ads, you have to register ad units.Open AdMob.Create an Ad unit for each of the AdMob apps.This asks for the Ad unit’s format. AdMob provides many formatsbeyond banner ads — interstitials, rewarded ads, app open ads, andso on. The API for those is similar, and documented in the AdMob documentationand through official samples.Choose banner ads.Get the Ad unit IDs for both the Android app and the iOS app.You can find these in the Ad units section. They look somethinglike ca-app-pub-1234567890123456/1234567890. The format resemblesthe App ID but with a slash (/) between the two numbers. Thisdistinguishes an Ad unit ID from an App ID.Add these Ad unit IDs to the constructor of BannerAd,depending on the target app platform.<code_start>final String adUnitId = Platform.isAndroid    // Use this ad unit on Android...    ? 'ca-app-pub-3940256099942544/6300978111'    // ... or this one on iOS.    : 'ca-app-pub-3940256099942544/2934735716';<code_end><topic_end><topic_start>8. Final touchesTo display the ads in a published app or game (as opposed to debug ortesting scenarios), your app must meet additional requirements:Your app must be reviewed and approved before it can fully serveads.Follow AdMob’s app readiness guidelines.For example, your app must be listed on at least one of thesupported stores such as Google Play Store or Apple App Store.You must create an app-ads.txtfile and publish it on your developer website.To learn more about app and game monetization, visit the official sitesof AdMoband Ad Manager.<topic_end><topic_start>9. Complete exampleThe following code implements a simple stateful widget that loads abanner ad and shows it.<code_start>import 'dart:io';import 'package:flutter/widgets.dart';import 'package:google_mobile_ads/google_mobile_ads.dart';class MyBannerAdWidget extends StatefulWidget {  /// The requested size of the banner. Defaults to [AdSize.banner].  final AdSize adSize;  /// The AdMob ad unit to show.  ///  /// TODO: replace this test ad unit with your own ad unit  final String adUnitId = Platform.isAndroid      // Use this ad unit on Android...      ? 'ca-app-pub-3940256099942544/6300978111'      // ... or this one on iOS.      : 'ca-app-pub-3940256099942544/2934735716';  MyBannerAdWidget({    super.key,    this.adSize = AdSize.banner,  });  @override  State<MyBannerAdWidget> createState() => _MyBannerAdWidgetState();}class _MyBannerAdWidgetState extends State<MyBannerAdWidget> {  /// The banner ad to show. This is `null` until the ad is actually loaded.  BannerAd? _bannerAd;  @override  Widget build(BuildContext context) {    return SafeArea(      child: SizedBox(        width: widget.adSize.width.toDouble(),        height: widget.adSize.height.toDouble(),        child: _bannerAd == null            // Nothing to render yet.            ? SizedBox()            // The actual ad.            : AdWidget(ad: _bannerAd!),      ),    );  }  @override  void initState() {    super.initState();    _loadAd();  }  @override  void dispose() {    _bannerAd?.dispose();    super.dispose();  }  /// Loads a banner ad.  void _loadAd() {    final bannerAd = BannerAd(      size: widget.adSize,      adUnitId: widget.adUnitId,      request: const AdRequest(),      listener: BannerAdListener(        // Called when an ad is successfully received.        onAdLoaded: (ad) {          if (!mounted) {            ad.dispose();            return;          }          setState(() {            _bannerAd = ad as BannerAd;          });        },        // Called when an ad request failed.        onAdFailedToLoad: (ad, error) {          debugPrint('BannerAd failed to load: $error');          ad.dispose();        },      ),    );    // Start loading.    bannerAd.load();  }}<code_end>lightbulb Tip  In many cases, you will want to load the ad outside a widget.For example, you can load it in a ChangeNotifier, a BLoC, a controller,  or whatever else you are using for app-level state. This way, you can  preload a banner ad in advance, and have it ready to show for when the  user navigates to a new screen.Verify that you have loaded the BannerAd instance before showing it with  an AdWidget, and that you dispose of the instance when it is no longer  needed.<topic_end><topic_start>Add multiplayer support using FirestoreMultiplayer games need a way to synchronize game states between players.Broadly speaking, two types of multiplayer games exist:High tick rate.These games need to synchronize game states many times per secondwith low latency.These would include action games, sports games, fighting games.Low tick rate.These games only need to synchronize game states occasionallywith latency having less impact.These would include card games, strategy games, puzzle games.This resembles the differentiation between real-time versus turn-basedgames, though the analogy falls short.For example, real-time strategy games run—as the name suggests—inreal-time, but that doesn’t correlate to a high tick rate.These games can simulate much of what happensin between player interactions on local machines.Therefore, they don’t need to synchronize game states that often.If you can choose low tick rates as a developer, you should.Low tick lowers latency requirements and server costs.Sometimes, a game requires high tick rates of synchronization.For those cases, solutions such as Firestore don’t make a good fit.Pick a dedicated multiplayer server solution such as Nakama.Nakama has a Dart package.If you expect that your game requires a low tick rate of synchronization,continue reading.This recipe demonstrates how to use thecloud_firestore packageto implement multiplayer capabilities in your game.This recipe doesn’t require a server.It uses two or more clients sharing game state using Cloud Firestore.<topic_end><topic_start>1. Prepare your game for multiplayerWrite your game code to allow changing the game statein response to both local events and remote events.A local event could be a player action or some game logic.A remote event could be a world update coming from the server.To simplify this cookbook recipe, start withthe card template that you’ll findin the flutter/games repository.Run the following command to clone that repository:Open the project in templates/card.info Note  You can ignore this step and follow the recipe with your own game  project. Adapt the code at appropriate places.<topic_end><topic_start>2. Install FirestoreCloud Firestore is a horizontally scaling,NoSQL document database in the cloud.It includes built-in live synchronization.This is perfect for our needs.It keeps the game state updated in the cloud database,so every player sees the same state.If you want a quick, 15-minute primer on Cloud Firestore,check out the following video:To add Firestore to your Flutter project,follow the first two steps of theGet started with Cloud Firestore guide:The desired outcomes include:You don’t need to write any Dart code in this step.As soon as you understand the step of writingDart code in that guide, return to this recipe.<topic_end><topic_start>3. Initialize FirestoreOpen lib/main.dart and import the plugins, as well as the firebase_options.dart file that was generated by flutterfire configure in the previous step.<code_start>import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_core/firebase_core.dart'; import 'firebase_options.dart';<code_end>Add the following code just above the call to runApp() in lib/main.dart:<code_start>WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(   options: DefaultFirebaseOptions.currentPlatform, );<code_end>This ensures that Firebase is initialized on game startup.Add the Firestore instance to the app. That way, any widget can access this instance. Widgets can also react to the instance missing, if needed.To do this with the card template, you can use the provider package (which is already installed as a dependency).Replace the boilerplate runApp(MyApp()) with the following:<code_start>runApp(   Provider.value(     value: FirebaseFirestore.instance,     child: MyApp(),   ), );<code_end>Put the provider above MyApp, not inside it. This enables you to test the app without Firebase.info Note   In case you are not working with the card template,   you must either install the provider package   or use your own method of accessing the FirebaseFirestore   instance from various parts of your codebase.<topic_end><topic_start>4. Create a Firestore controller classThough you can talk to Firestore directly,you should write a dedicated controller classto make the code more readable and maintainable.How you implement the controller depends on your gameand on the exact design of your multiplayer experience.For the case of the card template,you could synchronize the contents of the two circular playing areas.It’s not enough for a full multiplayer experience,but it’s a good start.To create a controller, copy,then paste the following code into a new file calledlib/multiplayer/firestore_controller.dart.<code_start>import 'dart:async';import 'package:cloud_firestore/cloud_firestore.dart';import 'package:flutter/foundation.dart';import 'package:logging/logging.dart';import '../game_internals/board_state.dart';import '../game_internals/playing_area.dart';import '../game_internals/playing_card.dart';class FirestoreController {  static final _log = Logger('FirestoreController');  final FirebaseFirestore instance;  final BoardState boardState;  /// For now, there is only one match. But in order to be ready  /// for match-making, put it in a Firestore collection called matches.  late final _matchRef = instance.collection('matches').doc('match_1');  late final _areaOneRef = _matchRef      .collection('areas')      .doc('area_one')      .withConverter<List<PlayingCard>>(          fromFirestore: _cardsFromFirestore, toFirestore: _cardsToFirestore);  late final _areaTwoRef = _matchRef      .collection('areas')      .doc('area_two')      .withConverter<List<PlayingCard>>(          fromFirestore: _cardsFromFirestore, toFirestore: _cardsToFirestore);  StreamSubscription? _areaOneFirestoreSubscription;  StreamSubscription? _areaTwoFirestoreSubscription;  StreamSubscription? _areaOneLocalSubscription;  StreamSubscription? _areaTwoLocalSubscription;  FirestoreController({required this.instance, required this.boardState}) {    // Subscribe to the remote changes (from Firestore).    _areaOneFirestoreSubscription = _areaOneRef.snapshots().listen((snapshot) {      _updateLocalFromFirestore(boardState.areaOne, snapshot);    });    _areaTwoFirestoreSubscription = _areaTwoRef.snapshots().listen((snapshot) {      _updateLocalFromFirestore(boardState.areaTwo, snapshot);    });    // Subscribe to the local changes in game state.    _areaOneLocalSubscription = boardState.areaOne.playerChanges.listen((_) {      _updateFirestoreFromLocalAreaOne();    });    _areaTwoLocalSubscription = boardState.areaTwo.playerChanges.listen((_) {      _updateFirestoreFromLocalAreaTwo();    });    _log.fine('Initialized');  }  void dispose() {    _areaOneFirestoreSubscription?.cancel();    _areaTwoFirestoreSubscription?.cancel();    _areaOneLocalSubscription?.cancel();    _areaTwoLocalSubscription?.cancel();    _log.fine('Disposed');  }  /// Takes the raw JSON snapshot coming from Firestore and attempts to  /// convert it into a list of [PlayingCard]s.  List<PlayingCard> _cardsFromFirestore(    DocumentSnapshot<Map<String, dynamic>> snapshot,    SnapshotOptions? options,  ) {    final data = snapshot.data()?['cards'] as List?;    if (data == null) {      _log.info('No data found on Firestore, returning empty list');      return [];    }    final list = List.castFrom<Object?, Map<String, Object?>>(data);    try {      return list.map((raw) => PlayingCard.fromJson(raw)).toList();    } catch (e) {      throw FirebaseControllerException(          'Failed to parse data from Firestore: $e');    }  }  /// Takes a list of [PlayingCard]s and converts it into a JSON object  /// that can be saved into Firestore.  Map<String, Object?> _cardsToFirestore(    List<PlayingCard> cards,    SetOptions? options,  ) {    return {'cards': cards.map((c) => c.toJson()).toList()};  }  /// Updates Firestore with the local state of [area].  Future<void> _updateFirestoreFromLocal(      PlayingArea area, DocumentReference<List<PlayingCard>> ref) async {    try {      _log.fine('Updating Firestore with local data (${area.cards}) ...');      await ref.set(area.cards);      _log.fine('... done updating.');    } catch (e) {      throw FirebaseControllerException(          'Failed to update Firestore with local data (${area.cards}): $e');    }  }  /// Sends the local state of `boardState.areaOne` to Firestore.  void _updateFirestoreFromLocalAreaOne() {    _updateFirestoreFromLocal(boardState.areaOne, _areaOneRef);  }  /// Sends the local state of `boardState.areaTwo` to Firestore.  void _updateFirestoreFromLocalAreaTwo() {    _updateFirestoreFromLocal(boardState.areaTwo, _areaTwoRef);  }  /// Updates the local state of [area] with the data from Firestore.  void _updateLocalFromFirestore(      PlayingArea area, DocumentSnapshot<List<PlayingCard>> snapshot) {    _log.fine('Received new data from Firestore (${snapshot.data()})');    final cards = snapshot.data() ?? [];    if (listEquals(cards, area.cards)) {      _log.fine('No change');    } else {      _log.fine('Updating local data with Firestore data ($cards)');      area.replaceWith(cards);    }  }}class FirebaseControllerException implements Exception {  final String message;  FirebaseControllerException(this.message);  @override  String toString() => 'FirebaseControllerException: $message';}<code_end>Notice the following features of this code:The controller’s constructor takes a BoardState.This enables the controller to manipulate the local state of the game.The controller subscribes to both local changes to update Firestoreand to remote changes to update the local state and UI.The fields _areaOneRef and _areaTwoRef areFirebase document references.They describe where the data for each area resides,and how to convert between the local Dart objects (List<PlayingCard>)and remote JSON objects (Map<String, dynamic>).The Firestore API lets us subscribe to these referenceswith .snapshots(), and write to them with .set().<topic_end><topic_start>5. Use the Firestore controllerOpen the file responsible for starting the play session: lib/play_session/play_session_screen.dart in the case of the card template. You instantiate the Firestore controller from this file.Import Firebase and the controller:<code_start>import 'package:cloud_firestore/cloud_firestore.dart'; import '../multiplayer/firestore_controller.dart';<code_end>Add a nullable field to the _PlaySessionScreenState class to contain a controller instance:<code_start>FirestoreController? _firestoreController;<code_end>In the initState() method of the same class, add code that tries to read the FirebaseFirestore instance and, if successful, constructs the controller. You added the FirebaseFirestore instance to main.dart in the Initialize Firestore step.<code_start>final firestore = context.read<FirebaseFirestore?>(); if (firestore == null) {   _log.warning("Firestore instance wasn't provided. "       'Running without _firestoreController.'); } else {   _firestoreController = FirestoreController(     instance: firestore,     boardState: _boardState,   ); }<code_end>Dispose of the controller using the dispose() method of the same class.<code_start>_firestoreController?.dispose();<code_end><topic_end><topic_start>6. Test the gameRun the game on two separate devices or in 2 different windows on the same device.Watch how adding a card to an area on one device makes it appear on the other one.Open the Firebase web console and navigate to your project’s Firestore Database.Watch how it updates the data in real time. You can even edit the data in the console and see all running clients update.<topic_end><topic_start>TroubleshootingThe most common issues you might encounter when testingFirebase integration include the following:<topic_end><topic_start>7. Next stepsAt this point, the game has near-instant anddependable synchronization of state across clients.It lacks actual game rules:what cards can be played when, and with what results.This depends on the game itself and is left to you to try.At this point, the shared state of the match only includesthe two playing areas and the cards within them.You can save other data into _matchRef, too,like who the players are and whose turn it is.If you’re unsure where to start,follow a Firestore codelab or twoto familiarize yourself with the API.At first, a single match should sufficefor testing your multiplayer game with colleagues and friends.As you approach the release date,think about authentication and match-making.Thankfully, Firebase provides abuilt-in way to authenticate usersand the Firestore database structure can handle multiple matches.Instead of a single match_1,you can populate the matches collection with as many records as needed.An online match can start in a “waiting” state,with only the first player present.Other players can see the “waiting” matches in some kind of lobby.Once enough players join a match, it becomes “active”.Once again, the exact implementation depends onthe kind of online experience you want.The basics remain the same:a large collection of documents,each representing one active or potential match.<topic_end><topic_start>Flutter News ToolkitThe Flutter News Toolkit enables you to acceleratedevelopment of a mobile news app.The toolkit assists you in building a customizedtemplate app with prebuilt features requiredfor most news apps, such authentication andmonetization. After generating yourtemplate app, your primary tasks are to connect to yourdata source, and to customize the UI to reflectyour brand.The Flutter News Toolkit includes critical features,such as:You can use these pre-integrated features out of the box,or modify and swap them with other functionality thatyou prefer.Generating your template app requires answeringa few simple questions, as described on theFlutter News Toolkit Overview doc page.For complete documentation on how to configure your project,create a template app, develop the app, how tohandle authentication, theming, work with an APIserver, and much more, check out theFlutter News Toolkit documentation.You might also check out:info Note  This is an early release of the news toolkit and,  while it has been tested by early adopters, it  might have issues or rough edges. Please don’t  hesitate to file an issue.<topic_end><topic_start>Building user interfaces with FlutterFlutter widgets are built using a modern framework that takesinspiration from React. The central idea is that you buildyour UI out of widgets. Widgets describe what their viewshould look like given their current configuration and state.When a widget’s state changes, the widget rebuilds its description,which the framework diffs against the previous description in orderto determine the minimal changes needed in the underlying rendertree to transition from one state to the next.info Note  If you would like to become better acquainted with Flutter by diving  into some code, check out building layouts,  and adding interactivity to your Flutter app.<topic_end><topic_start>Hello worldThe minimal Flutter app simply calls the runApp()function with a widget:<code_start>import 'package:flutter/material.dart';void main() {  runApp(    const Center(      child: Text(        'Hello, world!',        textDirection: TextDirection.ltr,      ),    ),  );}<code_end>The runApp() function takes the givenWidget and makes it the root of the widget tree.In this example, the widget tree consists of two widgets,the Center widget and its child, the Text widget.The framework forces the root widget to cover the screen,which means the text “Hello, world” ends up centered on screen.The text direction needs to be specified in this instance;when the MaterialApp widget is used,this is taken care of for you, as demonstrated later.When writing an app, you’ll commonly author new widgets thatare subclasses of either StatelessWidget or StatefulWidget,depending on whether your widget manages any state.A widget’s main job is to implement a build() function,which describes the widget in terms of other, lower-level widgets.The framework builds those widgets in turn until the processbottoms out in widgets that represent the underlying RenderObject,which computes and describes the geometry of the widget.<topic_end><topic_start>Basic widgetsFlutter comes with a suite of powerful basic widgets,of which the following are commonly used:Below are some simple widgets that combine these and other widgets:<code_start>import 'package:flutter/material.dart';class MyAppBar extends StatelessWidget {  const MyAppBar({required this.title, super.key});  // Fields in a Widget subclass are always marked "final".  final Widget title;  @override  Widget build(BuildContext context) {    return Container(      height: 56, // in logical pixels      padding: const EdgeInsets.symmetric(horizontal: 8),      decoration: BoxDecoration(color: Colors.blue[500]),      // Row is a horizontal, linear layout.      child: Row(        children: [          const IconButton(            icon: Icon(Icons.menu),            tooltip: 'Navigation menu',            onPressed: null, // null disables the button          ),          // Expanded expands its child          // to fill the available space.          Expanded(            child: title,          ),          const IconButton(            icon: Icon(Icons.search),            tooltip: 'Search',            onPressed: null,          ),        ],      ),    );  }}class MyScaffold extends StatelessWidget {  const MyScaffold({super.key});  @override  Widget build(BuildContext context) {    // Material is a conceptual piece    // of paper on which the UI appears.    return Material(      // Column is a vertical, linear layout.      child: Column(        children: [          MyAppBar(            title: Text(              'Example title',              style: Theme.of(context) //                  .primaryTextTheme                  .titleLarge,            ),          ),          const Expanded(            child: Center(              child: Text('Hello, world!'),            ),          ),        ],      ),    );  }}void main() {  runApp(    const MaterialApp(      title: 'My app', // used by the OS task switcher      home: SafeArea(        child: MyScaffold(),      ),    ),  );}<code_end>Be sure to have a uses-material-design: true entry in the fluttersection of your pubspec.yaml file. It allows you to use the predefinedset of Material icons. It’s generally a good idea to include this lineif you are using the Materials library.Many Material Design widgets need to be inside of a MaterialAppto display properly, in order to inherit theme data.Therefore, run the application with a MaterialApp.The MyAppBar widget creates a Container with a height of 56device-independent pixels with an internal padding of 8 pixels,both on the left and the right. Inside the container,MyAppBar uses a Row layout to organize its children.The middle child, the title widget, is marked as Expanded,which means it expands to fill any remaining available spacethat hasn’t been consumed by the other children.You can have multiple Expanded children and determine theratio in which they consume the available space using theflex argument to Expanded.The MyScaffold widget organizes its children in a vertical column.At the top of the column it places an instance of MyAppBar,passing the app bar a Text widget to use as its title.Passing widgets as arguments to other widgets is a powerful techniquethat lets you create generic widgets that can be reused in a widevariety of ways. Finally, MyScaffold uses anExpanded to fill the remaining space with its body,which consists of a centered message.For more information, check out Layouts.<topic_end><topic_start>Using Material ComponentsFlutter provides a number of widgets that help you build appsthat follow Material Design. A Material app starts with theMaterialApp widget, which builds a number of useful widgetsat the root of your app, including a Navigator,which manages a stack of widgets identified by strings,also known as “routes”. The Navigator lets you transition smoothlybetween screens of your application. Using the MaterialAppwidget is entirely optional but a good practice.<code_start>import 'package:flutter/material.dart';void main() {  runApp(    const MaterialApp(      title: 'Flutter Tutorial',      home: TutorialHome(),    ),  );}class TutorialHome extends StatelessWidget {  const TutorialHome({super.key});  @override  Widget build(BuildContext context) {    // Scaffold is a layout for    // the major Material Components.    return Scaffold(      appBar: AppBar(        leading: const IconButton(          icon: Icon(Icons.menu),          tooltip: 'Navigation menu',          onPressed: null,        ),        title: const Text('Example title'),        actions: const [          IconButton(            icon: Icon(Icons.search),            tooltip: 'Search',            onPressed: null,          ),        ],      ),      // body is the majority of the screen.      body: const Center(        child: Text('Hello, world!'),      ),      floatingActionButton: const FloatingActionButton(        tooltip: 'Add', // used by assistive technologies        onPressed: null,        child: Icon(Icons.add),      ),    );  }}<code_end>Now that the code has switched from MyAppBar and MyScaffold to theAppBar and Scaffold widgets, and from material.dart,the app is starting to look a bit more Material.For example, the app bar has a shadow and the title text inherits thecorrect styling automatically. A floating action button is also added.Notice that widgets are passed as arguments to other widgets.The Scaffold widget takes a number of different widgets asnamed arguments, each of which are placed in the Scaffoldlayout in the appropriate place. Similarly, theAppBar widget lets you pass in widgets for theleading widget, and the actions of the title widget.This pattern recurs throughout the framework and is something youmight consider when designing your own widgets.For more information, check out Material Components widgets.info Note  Material is one of the 2 bundled designs included with Flutter.  To create an iOS-centric design,  check out the Cupertino components package,  which has its own versions of  CupertinoApp, and CupertinoNavigationBar.<topic_end><topic_start>Handling gesturesMost applications include some form of user interaction with the system.The first step in building an interactive application is to detectinput gestures. See how that works by creating a simple button:<code_start>import 'package:flutter/material.dart';class MyButton extends StatelessWidget {  const MyButton({super.key});  @override  Widget build(BuildContext context) {    return GestureDetector(      onTap: () {        print('MyButton was tapped!');      },      child: Container(        height: 50,        padding: const EdgeInsets.all(8),        margin: const EdgeInsets.symmetric(horizontal: 8),        decoration: BoxDecoration(          borderRadius: BorderRadius.circular(5),          color: Colors.lightGreen[500],        ),        child: const Center(          child: Text('Engage'),        ),      ),    );  }}void main() {  runApp(    const MaterialApp(      home: Scaffold(        body: Center(          child: MyButton(),        ),      ),    ),  );}<code_end>The GestureDetector widget doesn’t have a visualrepresentation but instead detects gestures made by theuser. When the user taps the Container,the GestureDetector calls its onTap() callback, in thiscase printing a message to the console. You can useGestureDetector to detect a variety of input gestures,including taps, drags, and scales.Many widgets use a GestureDetector to provideoptional callbacks for other widgets. For example, theIconButton, ElevatedButton, andFloatingActionButton widgets have onPressed()callbacks that are triggered when the user taps the widget.For more information, check out Gestures in Flutter.<topic_end><topic_start>Changing widgets in response to inputSo far, this page has used only stateless widgets.Stateless widgets receive arguments from their parent widget,which they store in final member variables.When a widget is asked to build(), it uses these storedvalues to derive new arguments for the widgets it creates.In order to build more complex experiences—for example,to react in more interesting ways to user input—applicationstypically carry some state. Flutter uses StatefulWidgets to capturethis idea. StatefulWidgets are special widgets that know how to generateState objects, which are then used to hold state.Consider this basic example, using the ElevatedButton mentioned earlier:<code_start>import 'package:flutter/material.dart';class Counter extends StatefulWidget {  // This class is the configuration for the state.  // It holds the values (in this case nothing) provided  // by the parent and used by the build  method of the  // State. Fields in a Widget subclass are always marked  // "final".  const Counter({super.key});  @override  State<Counter> createState() => _CounterState();}class _CounterState extends State<Counter> {  int _counter = 0;  void _increment() {    setState(() {      // This call to setState tells the Flutter framework      // that something has changed in this State, which      // causes it to rerun the build method below so that      // the display can reflect the updated values. If you      // change _counter without calling setState(), then      // the build method won't be called again, and so      // nothing would appear to happen.      _counter++;    });  }  @override  Widget build(BuildContext context) {    // This method is rerun every time setState is called,    // for instance, as done by the _increment method above.    // The Flutter framework has been optimized to make    // rerunning build methods fast, so that you can just    // rebuild anything that needs updating rather than    // having to individually changes instances of widgets.    return Row(      mainAxisAlignment: MainAxisAlignment.center,      children: <Widget>[        ElevatedButton(          onPressed: _increment,          child: const Text('Increment'),        ),        const SizedBox(width: 16),        Text('Count: $_counter'),      ],    );  }}void main() {  runApp(    const MaterialApp(      home: Scaffold(        body: Center(          child: Counter(),        ),      ),    ),  );}<code_end>You might wonder why StatefulWidget and State are separate objects.In Flutter, these two types of objects have different life cycles.Widgets are temporary objects, used to construct a presentation ofthe application in its current state. State objects, on the otherhand, are persistent between calls tobuild(), allowing them to remember information.The example above accepts user input and directly usesthe result in its build() method.  In more complex applications,different parts of the widget hierarchy might beresponsible for different concerns; for example, onewidget might present a complex user interfacewith the goal of gathering specific information,such as a date or location, while another widget mightuse that information to change the overall presentation.In Flutter, change notifications flow “up” the widgethierarchy by way of callbacks, while current state flows“down” to the stateless widgets that do presentation.The common parent that redirects this flow is the State.The following slightly more complex example shows howthis works in practice:<code_start>import 'package:flutter/material.dart';class CounterDisplay extends StatelessWidget {  const CounterDisplay({required this.count, super.key});  final int count;  @override  Widget build(BuildContext context) {    return Text('Count: $count');  }}class CounterIncrementor extends StatelessWidget {  const CounterIncrementor({required this.onPressed, super.key});  final VoidCallback onPressed;  @override  Widget build(BuildContext context) {    return ElevatedButton(      onPressed: onPressed,      child: const Text('Increment'),    );  }}class Counter extends StatefulWidget {  const Counter({super.key});  @override  State<Counter> createState() => _CounterState();}class _CounterState extends State<Counter> {  int _counter = 0;  void _increment() {    setState(() {      ++_counter;    });  }  @override  Widget build(BuildContext context) {    return Row(      mainAxisAlignment: MainAxisAlignment.center,      children: <Widget>[        CounterIncrementor(onPressed: _increment),        const SizedBox(width: 16),        CounterDisplay(count: _counter),      ],    );  }}void main() {  runApp(    const MaterialApp(      home: Scaffold(        body: Center(          child: Counter(),        ),      ),    ),  );}<code_end>Notice the creation of two new stateless widgets,cleanly separating the concerns of displaying the counter(CounterDisplay) and changing the counter (CounterIncrementor).Although the net result is the same as the previous example,the separation of responsibility allows greater complexity tobe encapsulated in the individual widgets,while maintaining simplicity in the parent.For more information, check out:<topic_end><topic_start>Bringing it all togetherWhat follows is a more complete example that brings togetherthese concepts: A hypothetical shopping application displays variousproducts offered for sale, and maintains a shopping cart forintended purchases. Start by defining the presentation class,ShoppingListItem:<code_start>import 'package:flutter/material.dart';class Product {  const Product({required this.name});  final String name;}typedef CartChangedCallback = Function(Product product, bool inCart);class ShoppingListItem extends StatelessWidget {  ShoppingListItem({    required this.product,    required this.inCart,    required this.onCartChanged,  }) : super(key: ObjectKey(product));  final Product product;  final bool inCart;  final CartChangedCallback onCartChanged;  Color _getColor(BuildContext context) {    // The theme depends on the BuildContext because different    // parts of the tree can have different themes.    // The BuildContext indicates where the build is    // taking place and therefore which theme to use.    return inCart //        ? Colors.black54        : Theme.of(context).primaryColor;  }  TextStyle? _getTextStyle(BuildContext context) {    if (!inCart) return null;    return const TextStyle(      color: Colors.black54,      decoration: TextDecoration.lineThrough,    );  }  @override  Widget build(BuildContext context) {    return ListTile(      onTap: () {        onCartChanged(product, inCart);      },      leading: CircleAvatar(        backgroundColor: _getColor(context),        child: Text(product.name[0]),      ),      title: Text(product.name, style: _getTextStyle(context)),    );  }}void main() {  runApp(    MaterialApp(      home: Scaffold(        body: Center(          child: ShoppingListItem(            product: const Product(name: 'Chips'),            inCart: true,            onCartChanged: (product, inCart) {},          ),        ),      ),    ),  );}<code_end>The ShoppingListItem widget follows a common patternfor stateless widgets.  It stores the values it receivesin its constructor in final member variables,which it then uses during its build() function.For example, the inCart boolean toggles between two visualappearances: one that uses the primary color from the currenttheme, and another that uses gray.When the user taps the list item, the widget doesn’t modifyits inCart value directly. Instead, the widget calls theonCartChanged function it received from its parent widget.This pattern lets you store state higher in the widgethierarchy, which causes the state to persist for longer periods of time.In the extreme, the state stored on the widget passed torunApp() persists for the lifetime of theapplication.When the parent receives the onCartChanged callback,the parent updates its internal state, which triggersthe parent to rebuild and create a new instanceof ShoppingListItem with the new inCart value.Although the parent creates a new instance ofShoppingListItem when it rebuilds, that operation is cheapbecause the framework compares the newly built widgets with the previouslybuilt widgets and applies only the differences to the underlyingRenderObject.Here’s an example parent widget that stores mutable state:<code_start>import 'package:flutter/material.dart';class Product {  const Product({required this.name});  final String name;}typedef CartChangedCallback = Function(Product product, bool inCart);class ShoppingListItem extends StatelessWidget {  ShoppingListItem({    required this.product,    required this.inCart,    required this.onCartChanged,  }) : super(key: ObjectKey(product));  final Product product;  final bool inCart;  final CartChangedCallback onCartChanged;  Color _getColor(BuildContext context) {    // The theme depends on the BuildContext because different    // parts of the tree can have different themes.    // The BuildContext indicates where the build is    // taking place and therefore which theme to use.    return inCart //        ? Colors.black54        : Theme.of(context).primaryColor;  }  TextStyle? _getTextStyle(BuildContext context) {    if (!inCart) return null;    return const TextStyle(      color: Colors.black54,      decoration: TextDecoration.lineThrough,    );  }  @override  Widget build(BuildContext context) {    return ListTile(      onTap: () {        onCartChanged(product, inCart);      },      leading: CircleAvatar(        backgroundColor: _getColor(context),        child: Text(product.name[0]),      ),      title: Text(        product.name,        style: _getTextStyle(context),      ),    );  }}class ShoppingList extends StatefulWidget {  const ShoppingList({required this.products, super.key});  final List<Product> products;  // The framework calls createState the first time  // a widget appears at a given location in the tree.  // If the parent rebuilds and uses the same type of  // widget (with the same key), the framework re-uses  // the State object instead of creating a new State object.  @override  State<ShoppingList> createState() => _ShoppingListState();}class _ShoppingListState extends State<ShoppingList> {  final _shoppingCart = <Product>{};  void _handleCartChanged(Product product, bool inCart) {    setState(() {      // When a user changes what's in the cart, you need      // to change _shoppingCart inside a setState call to      // trigger a rebuild.      // The framework then calls build, below,      // which updates the visual appearance of the app.      if (!inCart) {        _shoppingCart.add(product);      } else {        _shoppingCart.remove(product);      }    });  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Shopping List'),      ),      body: ListView(        padding: const EdgeInsets.symmetric(vertical: 8),        children: widget.products.map((product) {          return ShoppingListItem(            product: product,            inCart: _shoppingCart.contains(product),            onCartChanged: _handleCartChanged,          );        }).toList(),      ),    );  }}void main() {  runApp(const MaterialApp(    title: 'Shopping App',    home: ShoppingList(      products: [        Product(name: 'Eggs'),        Product(name: 'Flour'),        Product(name: 'Chocolate chips'),      ],    ),  ));}<code_end>The ShoppingList class extends StatefulWidget,which means this widget stores mutable state.When the ShoppingList widget is first insertedinto the tree, the framework calls the createState() functionto create a fresh instance of _ShoppingListState to associate with thatlocation in the tree. (Notice that subclasses ofState are typically named with leading underscores toindicate that they are private implementation details.)When this widget’s parent rebuilds, the parent creates a new instanceof ShoppingList, but the framework reuses the _ShoppingListStateinstance that is already in the tree rather than callingcreateState again.To access properties of the current ShoppingList,the _ShoppingListState can use its widget property.If the parent rebuilds and creates a new ShoppingList,the _ShoppingListState rebuilds with the new widget value.If you wish to be notified when the widget property changes,override the didUpdateWidget() function, which is passedan oldWidget to let you compare the old widget withthe current widget.When handling the onCartChanged callback, the _ShoppingListStatemutates its internal state by either adding or removing a product from_shoppingCart. To signal to the framework that it changed its internalstate, it wraps those calls in a setState() call.Calling setState marks this widget as dirty and schedules it to be rebuiltthe next time your app needs to update the screen.If you forget to call setState when modifying the internalstate of a widget, the framework won’t know your widget isdirty and might not call the widget’s build() function,which means the user interface might not update to reflectthe changed state.  By managing state in this way,you don’t need to write separate code for creating andupdating child widgets. Instead, you simply implement the buildfunction, which handles both situations.<topic_end><topic_start>Responding to widget lifecycle eventsAfter calling createState() on the StatefulWidget,the framework inserts the new state object into the tree andthen calls initState() on the state object.A subclass of State can override initState to do workthat needs to happen just once. For example, override initStateto configure animations or to subscribe to platform services.Implementations of initState are required to startby calling super.initState.When a state object is no longer needed,the framework calls dispose() on the state object.Override the dispose function to do cleanup work.For example, override dispose to cancel timers or tounsubscribe from platform services. Implementations ofdispose typically end by calling super.dispose.For more information, check out State.<topic_end><topic_start>KeysUse keys to control which widgets the framework matches upwith other widgets when a widget rebuilds. By default, theframework matches widgets in the current and previous buildaccording to their runtimeType and the order in which they appear.With keys, the framework requires that the two widgets havethe same key as well as the same runtimeType.Keys are most useful in widgets that build many instances ofthe same type of widget. For example, the ShoppingList widget,which builds just enough ShoppingListItem instances tofill its visible region:Without keys, the first entry in the current buildwould always sync with the first entry in the previous build,even if, semantically, the first entry in the list justscrolled off screen and is no longer visible in the viewport.By assigning each entry in the list a “semantic” key,the infinite list can be more efficient because theframework syncs entries with matching semantic keysand therefore similar (or identical) visual appearances.Moreover, syncing the entries semantically means thatstate retained in stateful child widgets remains attachedto the same semantic entry rather than the entry in thesame numerical position in the viewport.For more information, check out the Key API.<topic_end><topic_start>Global keysUse global keys to uniquely identify child widgets.Global keys must be globally unique across the entirewidget hierarchy, unlike local keys which needonly be unique among siblings. Because they areglobally unique, a global key can be used toretrieve the state associated with a widget.For more information, check out the GlobalKey API.<topic_end><topic_start>Widget catalogCreate beautiful apps faster with Flutter’s collection of visual, structural,platform, and interactive widgets. In addition to browsing widgets by category,you can also see all the widgets in the widget index.Make your app accessible.Bring animations to your app.Manage assets, display images, and show icons.Async patterns to your Flutter application.Widgets you absolutely need to know before building your first Flutter app.Beautiful and high-fidelity widgets for current iOS design language.Take user input in addition to input widgets in Material components and Cupertino.Respond to touch events and route users to different views.Arrange other widgets columns, rows, grids, and many other layouts.Widgets implementing the Material 2 Design guidelines.Visual, behavioral, and motion-rich widgets implementing the Material 3 design specification.Material 3 is the default Flutter interface as of Flutter 3.16. To learn more about this transition, check out Flutter support for Material 3.These widgets apply visual effects to the children without changing their layout, size, or position.Scroll multiple widgets as children of the parent.Manage the theme of your app, makes your app responsive to screen sizes, or add padding.Display and style text.<topic_end><topic_start>Widget of the Week100+ short, 1-minute explainer videos tohelp you quickly get started with Flutter widgets.See more Widget of the Weeks<topic_end><topic_start>Layouts in Flutter<topic_end><topic_start>What's the point?The core of Flutter’s layout mechanism is widgets.In Flutter, almost everything is a widget—evenlayout models are widgets. The images, icons,and text that you see in a Flutter app are all widgets.But things you don’t see are also widgets,such as the rows, columns, and grids that arrange,constrain, and align the visible widgets.You create a layout by composing widgets to build more complex widgets.For example, the first screenshot below shows 3 icons with a labelunder each one:The second screenshot displays the visual layout, showing a row of3 columns where each column contains an icon and a label.info Note  Most of the screenshots in this tutorial are displayed with  debugPaintSizeEnabled set to true so you can see the visual layout.  For more information, see  Debugging layout issues visually, a section in  Using the Flutter inspector.Here’s a diagram of the widget tree for this UI:Most of this should look as you might expect, but you might be wonderingabout the containers (shown in pink). Container is a widget classthat allows you to customize its child widget. Use a Container whenyou want to add padding, margins, borders, or background color,to name some of its capabilities.In this example, each Text widget is placed in a Containerto add margins. The entire Row is also placed in aContainer to add padding around the row.The rest of the UI in this example is controlled by properties.Set an Icon’s color using its color property.Use the Text.style property to set the font, its color, weight, and so on.Columns and rows have properties that allow you to specify how theirchildren are aligned vertically or horizontally, and how much spacethe children should occupy.<topic_end><topic_start>Lay out a widgetHow do you lay out a single widget in Flutter? This sectionshows you how to create and display a simple widget.It also shows the entire code for a simple Hello World app.In Flutter, it takes only a few steps to put text, an icon,or an image on the screen.<topic_end><topic_start>1. Select a layout widgetChoose from a variety of layout widgets basedon how you want to align or constrain the visible widget,as these characteristics are typically passed on to thecontained widget.This example uses Center which centers its contenthorizontally and vertically.<topic_end><topic_start>2. Create a visible widgetFor example, create a Text widget:<code_start>Text('Hello World'),<code_end>Create an Image widget:<code_start>return Image.asset(  image,  fit: BoxFit.cover,);<code_end>Create an Icon widget:<code_start>Icon(  Icons.star,  color: Colors.red[500],),<code_end><topic_end><topic_start>3. Add the visible widget to the layout widgetAll layout widgets have either of the following:Add the Text widget to the Center widget:<code_start>const Center(  child: Text('Hello World'),),<code_end><topic_end><topic_start>4. Add the layout widget to the pageA Flutter app is itself a widget, and most widgets have a build()method. Instantiating and returning a widget in the app’s build() methoddisplays the widget.<topic_end><topic_start>Material appsFor a Material app, you can use a Scaffold widget;it provides a default banner, background color,and has API for adding drawers, snack bars, and bottom sheets.Then you can add the Center widget directly to the bodyproperty for the home page.<code_start>class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const String appTitle = 'Flutter layout demo';    return MaterialApp(      title: appTitle,      home: Scaffold(        appBar: AppBar(          title: const Text(appTitle),        ),        body: const Center(          child: Text('Hello World'),        ),      ),    );  }}<code_end>info Note  The Material library implements widgets that follow Material  Design principles. When designing your UI, you can exclusively use  widgets from the standard widgets library, or you can use  widgets from the Material library. You can mix widgets from both  libraries, you can customize existing widgets,  or you can build your own set of custom widgets.<topic_end><topic_start>Cupertino appsTo create a Cupertino app, use CupertinoApp and CupertinoPageScaffold widgets.Unlike Material, it doesn’t provide a default banner or background color.You need to set these yourself.To add an iOS-styled navigation bar to the top of your app, add aCupertinoNavigationBar widget to the navigationBarproperty of your scaffold.You can use the colors that CupertinoColors provides toconfigure your widgets to match iOS design.To learn what other UI components you can add, check out theCupertino library.<code_start>class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return const CupertinoApp(      title: 'Flutter layout demo',      theme: CupertinoThemeData(        brightness: Brightness.light,        primaryColor: CupertinoColors.systemBlue,      ),      home: CupertinoPageScaffold(        navigationBar: CupertinoNavigationBar(          backgroundColor: CupertinoColors.systemGrey,          middle: Text('Flutter layout demo'),        ),        child: Center(          child: Column(            mainAxisAlignment: MainAxisAlignment.center,            children: <Widget>[              Text('Hello World'),            ],          ),        ),      ),    );  }}<code_end>info Note  The Cupertino library implements widgets that follow  Apple’s Human Interface Guidelines for iOS.  When designing your UI, you can use  widgets from the standard widgets library, or the Cupertino library.  You can mix widgets from both libraries, you can customize existing widgets,  or you can build your own set of custom widgets.<topic_end><topic_start>Non-Material appsFor a non-Material app, you can add the Center widget to the app’sbuild() method:<code_start>class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return Container(      decoration: const BoxDecoration(color: Colors.white),      child: const Center(        child: Text(          'Hello World',          textDirection: TextDirection.ltr,          style: TextStyle(            fontSize: 32,            color: Colors.black87,          ),        ),      ),    );  }}<code_end>By default, a non-Material app doesn’t include an AppBar, title,or background color. If you want these features in a non-Material app,you have to build them yourself. This app changes the backgroundcolor to white and the text to dark grey to mimic a Material app.That’s it! When you run the app, you should see Hello World.App source code:<topic_end><topic_start>Lay out multiple widgets vertically and horizontallyOne of the most common layout patterns is to arrangewidgets vertically or horizontally. You can use aRow widget to arrange widgets horizontally,and a Column widget to arrange widgets vertically.<topic_end><topic_start>What's the point?To create a row or column in Flutter, you add a list of childrenwidgets to a Row or Column widget. In turn,each child can itself be a row or column, and so on.The following example shows how it is possible to nest rows orcolumns inside of rows or columns.This layout is organized as a Row. The row contains two children:a column on the left, and an image on the right:The left column’s widget tree nests rows and columns.You’ll implement some of Pavlova’s layout code inNesting rows and columns.info NoteRow and Column are basic primitive widgets for horizontal  and vertical layouts—these low-level widgets allow for maximum  customization. Flutter also offers specialized, higher level widgets  that might be sufficient for your needs. For example,  instead of Row you might prefer ListTile,  an easy-to-use widget with properties for leading and trailing icons,  and up to 3 lines of text.  Instead of Column, you might prefer  ListView, a column-like layout that automatically scrolls  if its content is too long to fit the available space.  For more information, see Common layout widgets.<topic_end><topic_start>Aligning widgetsYou control how a row or column aligns its children using themainAxisAlignment and crossAxisAlignment properties.For a row, the main axis runs horizontally and the cross axis runsvertically. For a column, the main axis runs vertically and the crossaxis runs horizontally.The MainAxisAlignment and CrossAxisAlignmentenums offer a variety of constants for controlling alignment.info Note  When you add images to your project,  you need to update the pubspec.yaml file to access  them—this example uses Image.asset to display  the images.  For more information, see this example’s  pubspec.yaml file or Adding assets and images.  You don’t need to do this if you’re referencing online  images using Image.network.In the following example, each of the 3 images is 100 pixels wide.The render box (in this case, the entire screen)is more than 300 pixels wide, so setting the main axisalignment to spaceEvenly divides the free horizontalspace evenly between, before, and after each image.App source: row_columnColumns work the same way as rows. The following example shows a columnof 3 images, each is 100 pixels high. The height of the render box(in this case, the entire screen) is more than 300 pixels, sosetting the main axis alignment to spaceEvenly divides the free verticalspace evenly between, above, and below each image.App source: row_column<topic_end><topic_start>Sizing widgetsWhen a layout is too large to fit a device, a yellowand black striped pattern appears along the affected edge.Here is an example of a row that is too wide:Widgets can be sized to fit within a row or column by using theExpanded widget. To fix the previous example where therow of images is too wide for its render box,wrap each image with an Expanded widget.App source: sizingPerhaps you want a widget to occupy twice as much space as itssiblings. For this, use the Expanded widget flex property,an integer that determines the flex factor for a widget.The default flex factor is 1. The following code setsthe flex factor of the middle image to 2:App source: sizing<topic_end><topic_start>Packing widgetsBy default, a row or column occupies as much space along its main axisas possible, but if you want to pack the children closely together,set its mainAxisSize to MainAxisSize.min. The following exampleuses this property to pack the star icons together.App source: pavlova<topic_end><topic_start>Nesting rows and columnsThe layout framework allows you to nest rows and columnsinside of rows and columns as deeply as you need.Let’s look at the code for the outlinedsection of the following layout:The outlined section is implemented as two rows. The ratings row containsfive stars and the number of reviews. The icons row contains threecolumns of icons and text.The widget tree for the ratings row:The ratings variable creates a row containing a smaller rowof 5 star icons, and text:<code_start>final stars = Row(  mainAxisSize: MainAxisSize.min,  children: [    Icon(Icons.star, color: Colors.green[500]),    Icon(Icons.star, color: Colors.green[500]),    Icon(Icons.star, color: Colors.green[500]),    const Icon(Icons.star, color: Colors.black),    const Icon(Icons.star, color: Colors.black),  ],);final ratings = Container(  padding: const EdgeInsets.all(20),  child: Row(    mainAxisAlignment: MainAxisAlignment.spaceEvenly,    children: [      stars,      const Text(        '170 Reviews',        style: TextStyle(          color: Colors.black,          fontWeight: FontWeight.w800,          fontFamily: 'Roboto',          letterSpacing: 0.5,          fontSize: 20,        ),      ),    ],  ),);<code_end>lightbulb Tip  To minimize the visual confusion that can result from  heavily nested layout code, implement pieces of the UI  in variables and functions.The icons row, below the ratings row, contains 3 columns;each column contains an icon and two lines of text,as you can see in its widget tree:The iconList variable defines the icons row:<code_start>const descTextStyle = TextStyle(  color: Colors.black,  fontWeight: FontWeight.w800,  fontFamily: 'Roboto',  letterSpacing: 0.5,  fontSize: 18,  height: 2,);// DefaultTextStyle.merge() allows you to create a default text// style that is inherited by its child and all subsequent children.final iconList = DefaultTextStyle.merge(  style: descTextStyle,  child: Container(    padding: const EdgeInsets.all(20),    child: Row(      mainAxisAlignment: MainAxisAlignment.spaceEvenly,      children: [        Column(          children: [            Icon(Icons.kitchen, color: Colors.green[500]),            const Text('PREP:'),            const Text('25 min'),          ],        ),        Column(          children: [            Icon(Icons.timer, color: Colors.green[500]),            const Text('COOK:'),            const Text('1 hr'),          ],        ),        Column(          children: [            Icon(Icons.restaurant, color: Colors.green[500]),            const Text('FEEDS:'),            const Text('4-6'),          ],        ),      ],    ),  ),);<code_end>The leftColumn variable contains the ratings and icons rows,as well as the title and text that describes the Pavlova:<code_start>final leftColumn = Container(  padding: const EdgeInsets.fromLTRB(20, 30, 20, 20),  child: Column(    children: [      titleText,      subTitle,      ratings,      iconList,    ],  ),);<code_end>The left column is placed in a SizedBox to constrain its width.Finally, the UI is constructed with the entire row (containing theleft column and the image) inside a Card.The Pavlova image is from Pixabay.You can embed an image from the net using Image.network() but,for this example, the image is saved to an images directory in the project,added to the pubspec file, and accessed using Images.asset().For more information, see Adding assets and images.<code_start>body: Center(  child: Container(    margin: const EdgeInsets.fromLTRB(0, 40, 0, 30),    height: 600,    child: Card(      child: Row(        crossAxisAlignment: CrossAxisAlignment.start,        children: [          SizedBox(            width: 440,            child: leftColumn,          ),          mainImage,        ],      ),    ),  ),),<code_end>lightbulb Tip  The Pavlova example runs best horizontally on a wide device,  such as a tablet.  If you are running this example in the iOS simulator,  you can select a different device using the Hardware > Device menu.  For this example, we recommend the iPad Pro.  You can change its orientation to landscape mode using  Hardware > Rotate. You can also change the size of the  simulator window (without changing the number of logical pixels)  using Window > Scale.App source: pavlova<topic_end><topic_start>Common layout widgetsFlutter has a rich library of layout widgets.Here are a few of those most commonly used.The intent is to get you up and running as quickly as possible,rather than overwhelm you with a complete list.For information on other available widgets,refer to the Widget catalog,or use the Search box in the API reference docs.Also, the widget pages in the API docs often make suggestionsabout similar widgets that might better suit your needs.The following widgets fall into two categories: standard widgetsfrom the widgets library, and specialized widgets from theMaterial library. Any app can use the widgets library butonly Material apps can use the Material Components library.<topic_end><topic_start>Standard widgets<topic_end><topic_start>Material widgets<topic_end><topic_start>ContainerMany layouts make liberal use of Containers to separatewidgets using padding, or to add borders or margins.You can change the device’s background by placing theentire layout into a Container and changing its backgroundcolor or image.<topic_end><topic_start>Summary (Container)<topic_end><topic_start>Examples (Container)This layout consists of a column with two rows, each containing2 images. A Container is used to change the background colorof the column to a lighter grey.A Container is also used to add a rounded border and marginsto each image:<code_start>Widget _buildDecoratedImage(int imageIndex) => Expanded(      child: Container(        decoration: BoxDecoration(          border: Border.all(width: 10, color: Colors.black38),          borderRadius: const BorderRadius.all(Radius.circular(8)),        ),        margin: const EdgeInsets.all(4),        child: Image.asset('images/pic$imageIndex.jpg'),      ),    );Widget _buildImageRow(int imageIndex) => Row(      children: [        _buildDecoratedImage(imageIndex),        _buildDecoratedImage(imageIndex + 1),      ],    );<code_end>You can find more Container examples in the tutorial.App source: container<topic_end><topic_start>GridViewUse GridView to lay widgets out as a two-dimensionallist. GridView provides two pre-fabricated lists,or you can build your own custom grid. When a GridViewdetects that its contents are too long to fit the render box,it automatically scrolls.<topic_end><topic_start>Summary (GridView)info Note  When displaying a two-dimensional list where it’s important which  row and column a cell occupies (for example,  it’s the entry in the “calorie” column for the “avocado” row), use  Table or DataTable.<topic_end><topic_start>Examples (GridView)Uses GridView.extent to create a grid with tiles a maximum  150 pixels wide.App source: grid_and_listUses GridView.count to create a grid that’s 2 tiles  wide in portrait mode, and 3 tiles wide in landscape mode.  The titles are created by setting the footer property for  each GridTile.Dart code:grid_list_demo.dart<code_start>Widget _buildGrid() => GridView.extent(    maxCrossAxisExtent: 150,    padding: const EdgeInsets.all(4),    mainAxisSpacing: 4,    crossAxisSpacing: 4,    children: _buildGridTileList(30));// The images are saved with names pic0.jpg, pic1.jpg...pic29.jpg.// The List.generate() constructor allows an easy way to create// a list when objects have a predictable naming pattern.List<Container> _buildGridTileList(int count) => List.generate(    count, (i) => Container(child: Image.asset('images/pic$i.jpg')));<code_end><topic_end><topic_start>ListViewListView, a column-like widget, automaticallyprovides scrolling when its content is too long forits render box.<topic_end><topic_start>Summary (ListView)<topic_end><topic_start>Examples (ListView)Uses ListView to display a list of businesses using  ListTiles. A Divider separates the theaters from  the restaurants.App source: grid_and_listUses ListView to display the Colors from  the Material 2 Design palette  for a particular color family.Dart code:colors_demo.dart<code_start>Widget _buildList() {  return ListView(    children: [      _tile('CineArts at the Empire', '85 W Portal Ave', Icons.theaters),      _tile('The Castro Theater', '429 Castro St', Icons.theaters),      _tile('Alamo Drafthouse Cinema', '2550 Mission St', Icons.theaters),      _tile('Roxie Theater', '3117 16th St', Icons.theaters),      _tile('United Artists Stonestown Twin', '501 Buckingham Way',          Icons.theaters),      _tile('AMC Metreon 16', '135 4th St #3000', Icons.theaters),      const Divider(),      _tile('K\'s Kitchen', '757 Monterey Blvd', Icons.restaurant),      _tile('Emmy\'s Restaurant', '1923 Ocean Ave', Icons.restaurant),      _tile('Chaiya Thai Restaurant', '272 Claremont Blvd', Icons.restaurant),      _tile('La Ciccia', '291 30th St', Icons.restaurant),    ],  );}ListTile _tile(String title, String subtitle, IconData icon) {  return ListTile(    title: Text(title,        style: const TextStyle(          fontWeight: FontWeight.w500,          fontSize: 20,        )),    subtitle: Text(subtitle),    leading: Icon(      icon,      color: Colors.blue[500],    ),  );}<code_end><topic_end><topic_start>StackUse Stack to arrange widgets on top of a basewidget—often an image. The widgets can completelyor partially overlap the base widget.<topic_end><topic_start>Summary (Stack)<topic_end><topic_start>Examples (Stack)Uses Stack to overlay a Container  (that displays its Text on a translucent  black background) on top of a CircleAvatar.  The Stack offsets the text using the alignment property and  Alignments.App source: card_and_stackUses Stack to overlay an icon on top of an image.Dart code:bottom_navigation_demo.dart<code_start>Widget _buildStack() {  return Stack(    alignment: const Alignment(0.6, 0.6),    children: [      const CircleAvatar(        backgroundImage: AssetImage('images/pic.jpg'),        radius: 100,      ),      Container(        decoration: const BoxDecoration(          color: Colors.black45,        ),        child: const Text(          'Mia B',          style: TextStyle(            fontSize: 20,            fontWeight: FontWeight.bold,            color: Colors.white,          ),        ),      ),    ],  );}<code_end><topic_end><topic_start>CardA Card, from the Material library,contains related nuggets of information and canbe composed from almost any widget, but is often used withListTile. Card has a single child,but its child can be a column, row, list, grid,or other widget that supports multiple children.By default, a Card shrinks its size to 0 by 0 pixels.You can use SizedBox to constrain the size of a card.In Flutter, a Card features slightly rounded cornersand a drop shadow, giving it a 3D effect.Changing a Card’s elevation property allows you to controlthe drop shadow effect. Setting the elevation to 24,for example, visually lifts the Card further from thesurface and causes the shadow to become more dispersed.For a list of supported elevation values, see Elevation in theMaterial guidelines.Specifying an unsupported value disables the drop shadow entirely.<topic_end><topic_start>Summary (Card)<topic_end><topic_start>Examples (Card)A Card containing 3 ListTiles and sized by wrapping  it with a SizedBox. A Divider separates the first  and second ListTiles.App source: card_and_stackA Card containing an image and text.Dart code:cards_demo.dart<code_start>Widget _buildCard() {  return SizedBox(    height: 210,    child: Card(      child: Column(        children: [          ListTile(            title: const Text(              '1625 Main Street',              style: TextStyle(fontWeight: FontWeight.w500),            ),            subtitle: const Text('My City, CA 99984'),            leading: Icon(              Icons.restaurant_menu,              color: Colors.blue[500],            ),          ),          const Divider(),          ListTile(            title: const Text(              '(408) 555-1212',              style: TextStyle(fontWeight: FontWeight.w500),            ),            leading: Icon(              Icons.contact_phone,              color: Colors.blue[500],            ),          ),          ListTile(            title: const Text('costa@example.com'),            leading: Icon(              Icons.contact_mail,              color: Colors.blue[500],            ),          ),        ],      ),    ),  );}<code_end><topic_end><topic_start>ListTileUse ListTile, a specialized row widget from theMaterial library, for an easy way to create a rowcontaining up to 3 lines of text and optional leadingand trailing icons. ListTile is most commonly used inCard or ListView, but can be used elsewhere.<topic_end><topic_start>Summary (ListTile)<topic_end><topic_start>Examples (ListTile)A Card containing 3 ListTiles.App source: card_and_stackUses ListTile with leading widgets.Dart code:list_demo.dart<topic_end><topic_start>ConstraintsTo fully understand Flutter’s layout system, you needto learn how Flutter positions and sizesthe components in a layout. For more information,see Understanding constraints.<topic_end><topic_start>VideosThe following videos, part of theFlutter in Focus series,explain Stateless and Stateful widgets.Flutter in Focus playlistEach episode of theWidget of the Week seriesfocuses on a widget. Several of them includes layout widgets.Flutter Widget of the Week playlist<topic_end><topic_start>Other resourcesThe following resources might help when writing layout code.<topic_end><topic_start>Build a Flutter layout<topic_end><topic_start>What you’ll learnThis tutorial explains how to design and build layouts in Flutter.If you use the example code provided, you can build the following app.Photo by Dino Reichmuth on Unsplash.Text by Switzerland Tourism.To get a better overview of the layout mechanism, start withFlutter’s approach to layout.<topic_end><topic_start>Diagram the layoutIn this section, consider what type of user experience you want foryour app users.Consider how to position the components of your user interface.A layout consists of the total end result of these positionings.Consider planning your layout to speed up your coding.Using visual cues to know where something goes on screen can be a great help.Use whichever method you prefer, like an interface design tool or a penciland a sheet of paper. Figure out where you want to place elements on yourscreen before writing code. It’s the programming version of the adage:“Measure twice, cut once.”Ask these questions to break the layout down to its basic elements.Identify the larger elements. In this example, you arrange the image, title,buttons, and description into a column.Diagram each row.Row 1, the Title section, has three children:a column of text, a star icon, and a number.Its first child, the column, contains two lines of text.That first column might need more space.Row 2, the Button section, has three children: each child containsa column which then contains an icon and text.After diagramming the layout, consider how you would code it.Would you write all the code in one class?Or, would you create one class for each part of the layout?To follow Flutter best practices, create one class, or Widget,to contain each part of your layout.When Flutter needs to re-render part of a UI,it updates the smallest part that changes.This is why Flutter makes “everything a widget”.If only the text changes in a Text widget, Flutter redraws only that text.Flutter changes the least amount of the UI possible in response to user input.For this tutorial, write each element you have identified as its own widget.<topic_end><topic_start>Create the app base codeIn this section, shell out the basic Flutter app code to start your app.Set up your Flutter environment.Create a new Flutter app.Replace the contents of lib/main.dart with the following code.This app uses a parameter for the app title and the title shownon the app’s appBar. This decision simplifies the code.<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const String appTitle = 'Flutter layout demo';    return MaterialApp(      title: appTitle,      home: Scaffold(        appBar: AppBar(          title: const Text(appTitle),        ),        body: const Center(          child: Text('Hello World'),        ),      ),    );  }}<code_end><topic_end><topic_start>Add the Title sectionIn this section, create a TitleSection widget that resemblesthe following layout.<topic_end><topic_start>Add the TitleSection WidgetAdd the following code after the MyApp class.<code_start>class TitleSection extends StatelessWidget {  const TitleSection({    super.key,    required this.name,    required this.location,  });  final String name;  final String location;  @override  Widget build(BuildContext context) {    return Padding(      padding: const EdgeInsets.all(32),      child: Row(        children: [          Expanded(            /*1*/            child: Column(              crossAxisAlignment: CrossAxisAlignment.start,              children: [                /*2*/                Padding(                  padding: const EdgeInsets.only(bottom: 8),                  child: Text(                    name,                    style: const TextStyle(                      fontWeight: FontWeight.bold,                    ),                  ),                ),                Text(                  location,                  style: TextStyle(                    color: Colors.grey[500],                  ),                ),              ],            ),          ),          /*3*/          Icon(            Icons.star,            color: Colors.red[500],          ),          const Text('41'),        ],      ),    );  }}<code_end><topic_end><topic_start>Change the app body to a scrolling viewIn the body property, replace the Center widget with aSingleChildScrollView widget.Within the SingleChildScrollView widget, replace the Text widget with aColumn widget.These code updates change the app in the following ways.<topic_end><topic_start>Update the app to display the title sectionAdd the TitleSection widget as the first element in the children list.This places it at the top of the screen.Pass the provided name and location to the TitleSection constructor.lightbulb Tip<topic_end><topic_start>Add the Button sectionIn this section, add the buttons that will add functionality to your app.The Button section contains three columns that use the same layout:an icon over a row of text.Plan to distribute these columns in one row so each takes the sameamount of space. Paint all text and icons with the primary color.<topic_end><topic_start>Add the ButtonSection widgetAdd the following code after the TitleSection widget to contain the codeto build the row of buttons.<code_start>class ButtonSection extends StatelessWidget {  const ButtonSection({super.key});  @override  Widget build(BuildContext context) {    final Color color = Theme.of(context).primaryColor;// ···  }}<code_end><topic_end><topic_start>Create a widget to make buttonsAs the code for each column could use the same syntax,create a widget named ButtonWithText.The widget’s constructor accepts a color, icon data, and a label for the button.Using these values, the widget builds a Column with an Icon and a stylizedText widget as its children.To help separate these children, a Padding widget the Text widgetis wrapped with a Padding widget.Add the following code after the ButtonSection class.<code_start>class ButtonSection extends StatelessWidget {  const ButtonSection({super.key});// ···}class ButtonWithText extends StatelessWidget {  const ButtonWithText({    super.key,    required this.color,    required this.icon,    required this.label,  });  final Color color;  final IconData icon;  final String label;  @override  Widget build(BuildContext context) {    return Column(      mainAxisSize: MainAxisSize.min,      mainAxisAlignment: MainAxisAlignment.center,      children: [        Icon(icon, color: color),        Padding(          padding: const EdgeInsets.only(top: 8),          child: Text(            label,            style: TextStyle(              fontSize: 12,              fontWeight: FontWeight.w400,              color: color,            ),          ),        ),      ],    );  }<code_end><topic_end><topic_start>Position the buttons with a Row widgetAdd the following code into the ButtonSection widget.<code_start>class ButtonSection extends StatelessWidget {  const ButtonSection({super.key});  @override  Widget build(BuildContext context) {    final Color color = Theme.of(context).primaryColor;    return SizedBox(      child: Row(        mainAxisAlignment: MainAxisAlignment.spaceEvenly,        children: [          ButtonWithText(            color: color,            icon: Icons.call,            label: 'CALL',          ),          ButtonWithText(            color: color,            icon: Icons.near_me,            label: 'ROUTE',          ),          ButtonWithText(            color: color,            icon: Icons.share,            label: 'SHARE',          ),        ],      ),    );  }}class ButtonWithText extends StatelessWidget {  const ButtonWithText({    super.key,    required this.color,    required this.icon,    required this.label,  });  final Color color;  final IconData icon;  final String label;  @override  Widget build(BuildContext context) {    return Column(// ···    );  }}<code_end><topic_end><topic_start>Update the app to display the button sectionAdd the button section to the children list.<topic_end><topic_start>Add the Text sectionIn this section, add the text description to this app.<topic_end><topic_start>Add the TextSection widgetAdd the following code as a separate widget after the ButtonSection widget.<code_start>class TextSection extends StatelessWidget {  const TextSection({    super.key,    required this.description,  });  final String description;  @override  Widget build(BuildContext context) {    return Padding(      padding: const EdgeInsets.all(32),      child: Text(        description,        softWrap: true,      ),    );  }}<code_end>By setting softWrap to true, text lines fill the column width beforewrapping at a word boundary.<topic_end><topic_start>Update the app to display the text sectionAdd a new TextSection widget as a child after the ButtonSection.When adding the TextSection widget, set its description property tothe text of the location description.<topic_end><topic_start>Add the Image sectionIn this section, add the image file to complete your layout.<topic_end><topic_start>Configure your app to use supplied imagesTo configure your app to reference images, modify its pubspec.yaml file.Create an images directory at the top of the project.Download the lake.jpg image and add it to the new images directory.info  You can’t use wget to save this binary file.  You can download the image from Unsplash  under the Unsplash License. The small size comes in at 94.4 kB.To include images, add an assets tag to the pubspec.yaml fileat the root directory of your app.When you add assets, it serves as the set of pointers to the imagesavailable to your code.lightbulb TipText in the pubspec.yaml respects whitespace and text case.Write the changes to the file as given in the previous example.This change might require you to restart the running program todisplay the image.<topic_end><topic_start>Create the ImageSection widgetDefine the following ImageSection widget after the other declarations.<code_start>class ImageSection extends StatelessWidget {  const ImageSection({super.key, required this.image});  final String image;  @override  Widget build(BuildContext context) {    return Image.asset(      image,      width: 600,      height: 240,      fit: BoxFit.cover,    );  }}<code_end>The BoxFit.cover value tells Flutter to display the image withtwo constraints. First, display the image as small as possible.Second, cover all the space that the layout allotted, called the render box.<topic_end><topic_start>Update the app to display the image sectionAdd an ImageSection widget as the first child in the children list.Set the image property to the path of the image you added inConfigure your app to use supplied images.<topic_end><topic_start>CongratulationsThat’s it! When you hot reload the app, your app should look like this.<topic_end><topic_start>ResourcesYou can access the resources used in this tutorial from these locations:Dart code: main.dartImage: ch-photoPubspec: pubspec.yaml<topic_end><topic_start>Next StepsTo add interactivity to this layout, follow theinteractivity tutorial.<topic_end><topic_start>Lists & grids<topic_end><topic_start>Topics<topic_end><topic_start>Use listsDisplaying lists of data is a fundamental pattern for mobile apps.Flutter includes the ListViewwidget to make working with lists a breeze.<topic_end><topic_start>Create a ListViewUsing the standard ListView constructor isperfect for lists that contain only a few items.The built-in ListTilewidget is a way to give items a visual structure.<code_start>ListView(  children: const <Widget>[    ListTile(      leading: Icon(Icons.map),      title: Text('Map'),    ),    ListTile(      leading: Icon(Icons.photo_album),      title: Text('Album'),    ),    ListTile(      leading: Icon(Icons.phone),      title: Text('Phone'),    ),  ],),<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const title = 'Basic List';    return MaterialApp(      title: title,      home: Scaffold(        appBar: AppBar(          title: const Text(title),        ),        body: ListView(          children: const <Widget>[            ListTile(              leading: Icon(Icons.map),              title: Text('Map'),            ),            ListTile(              leading: Icon(Icons.photo_album),              title: Text('Album'),            ),            ListTile(              leading: Icon(Icons.phone),              title: Text('Phone'),            ),          ],        ),      ),    );  }}<code_end><topic_end><topic_start>Create a horizontal listYou might want to create a list that scrollshorizontally rather than vertically.The ListView widget supports horizontal lists.Use the standard ListView constructor, passing in a horizontalscrollDirection, which overrides the default vertical direction.<code_start>ListView(  // This next line does the trick.  scrollDirection: Axis.horizontal,  children: <Widget>[    Container(      width: 160,      color: Colors.red,    ),    Container(      width: 160,      color: Colors.blue,    ),    Container(      width: 160,      color: Colors.green,    ),    Container(      width: 160,      color: Colors.yellow,    ),    Container(      width: 160,      color: Colors.orange,    ),  ],),<code_end><topic_end><topic_start>Interactive exampleDesktop and web only:  This example works in the browser and on the desktop.  However, as this list scrolls on the horizontal axis  (left to right or right to left),  hold Shift while using the mouse scroll wheel to scroll the list.To learn more, read the breaking change page on the  default drag for scrolling devices.<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const title = 'Horizontal List';    return MaterialApp(      title: title,      home: Scaffold(        appBar: AppBar(          title: const Text(title),        ),        body: Container(          margin: const EdgeInsets.symmetric(vertical: 20),          height: 200,          child: ListView(            // This next line does the trick.            scrollDirection: Axis.horizontal,            children: <Widget>[              Container(                width: 160,                color: Colors.red,              ),              Container(                width: 160,                color: Colors.blue,              ),              Container(                width: 160,                color: Colors.green,              ),              Container(                width: 160,                color: Colors.yellow,              ),              Container(                width: 160,                color: Colors.orange,              ),            ],          ),        ),      ),    );  }}<code_end><topic_end><topic_start>Create a grid listIn some cases, you might want to display your items as a grid rather thana normal list of items that come one after the next.For this task, use the GridView widget.The simplest way to get started using grids is by using theGridView.count() constructor,because it allows you to specify how many rows or columns you’d like.To visualize how GridView works,generate a list of 100 widgets that display their index in the list.<code_start>GridView.count(  // Create a grid with 2 columns. If you change the scrollDirection to  // horizontal, this produces 2 rows.  crossAxisCount: 2,  // Generate 100 widgets that display their index in the List.  children: List.generate(100, (index) {    return Center(      child: Text(        'Item $index',        style: Theme.of(context).textTheme.headlineSmall,      ),    );  }),),<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() {  runApp(const MyApp());}class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const title = 'Grid List';    return MaterialApp(      title: title,      home: Scaffold(        appBar: AppBar(          title: const Text(title),        ),        body: GridView.count(          // Create a grid with 2 columns. If you change the scrollDirection to          // horizontal, this produces 2 rows.          crossAxisCount: 2,          // Generate 100 widgets that display their index in the List.          children: List.generate(100, (index) {            return Center(              child: Text(                'Item $index',                style: Theme.of(context).textTheme.headlineSmall,              ),            );          }),        ),      ),    );  }}<code_end><topic_end><topic_start>Create lists with different types of itemsYou might need to create lists that display different types of content.For example, you might be working on a list that shows a headingfollowed by a few items related to the heading, followed by another heading,and so on.Here’s how you can create such a structure with Flutter:<topic_end><topic_start>1. Create a data source with different types of items<topic_end><topic_start>Types of itemsTo represent different types of items in a list, definea class for each type of item.In this example, create an app that shows a header followed by fivemessages. Therefore, create three classes: ListItem, HeadingItem,and MessageItem.<code_start>/// The base class for the different types of items the list can contain.abstract class ListItem {  /// The title line to show in a list item.  Widget buildTitle(BuildContext context);  /// The subtitle line, if any, to show in a list item.  Widget buildSubtitle(BuildContext context);}/// A ListItem that contains data to display a heading.class HeadingItem implements ListItem {  final String heading;  HeadingItem(this.heading);  @override  Widget buildTitle(BuildContext context) {    return Text(      heading,      style: Theme.of(context).textTheme.headlineSmall,    );  }  @override  Widget buildSubtitle(BuildContext context) => const SizedBox.shrink();}/// A ListItem that contains data to display a message.class MessageItem implements ListItem {  final String sender;  final String body;  MessageItem(this.sender, this.body);  @override  Widget buildTitle(BuildContext context) => Text(sender);  @override  Widget buildSubtitle(BuildContext context) => Text(body);}<code_end><topic_end><topic_start>Create a list of itemsMost of the time, you would fetch data from the internet or a localdatabase and convert that data into a list of items.For this example, generate a list of items to work with. The listcontains a header followed by five messages. Each message has oneof 3 types: ListItem, HeadingItem, or MessageItem.<code_start>final items = List<ListItem>.generate(  1000,  (i) => i % 6 == 0      ? HeadingItem('Heading $i')      : MessageItem('Sender $i', 'Message body $i'),);<code_end><topic_end><topic_start>2. Convert the data source into a list of widgetsTo convert each item into a widget,use the ListView.builder() constructor.In general, provide a builder function that checks for what typeof item you’re dealing with, and returns the appropriate widgetfor that type of item.<code_start>ListView.builder(  // Let the ListView know how many items it needs to build.  itemCount: items.length,  // Provide a builder function. This is where the magic happens.  // Convert each item into a widget based on the type of item it is.  itemBuilder: (context, index) {    final item = items[index];    return ListTile(      title: item.buildTitle(context),      subtitle: item.buildSubtitle(context),    );  },)<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() {  runApp(    MyApp(      items: List<ListItem>.generate(        1000,        (i) => i % 6 == 0            ? HeadingItem('Heading $i')            : MessageItem('Sender $i', 'Message body $i'),      ),    ),  );}class MyApp extends StatelessWidget {  final List<ListItem> items;  const MyApp({super.key, required this.items});  @override  Widget build(BuildContext context) {    const title = 'Mixed List';    return MaterialApp(      title: title,      home: Scaffold(        appBar: AppBar(          title: const Text(title),        ),        body: ListView.builder(          // Let the ListView know how many items it needs to build.          itemCount: items.length,          // Provide a builder function. This is where the magic happens.          // Convert each item into a widget based on the type of item it is.          itemBuilder: (context, index) {            final item = items[index];            return ListTile(              title: item.buildTitle(context),              subtitle: item.buildSubtitle(context),            );          },        ),      ),    );  }}/// The base class for the different types of items the list can contain.abstract class ListItem {  /// The title line to show in a list item.  Widget buildTitle(BuildContext context);  /// The subtitle line, if any, to show in a list item.  Widget buildSubtitle(BuildContext context);}/// A ListItem that contains data to display a heading.class HeadingItem implements ListItem {  final String heading;  HeadingItem(this.heading);  @override  Widget buildTitle(BuildContext context) {    return Text(      heading,      style: Theme.of(context).textTheme.headlineSmall,    );  }  @override  Widget buildSubtitle(BuildContext context) => const SizedBox.shrink();}/// A ListItem that contains data to display a message.class MessageItem implements ListItem {  final String sender;  final String body;  MessageItem(this.sender, this.body);  @override  Widget buildTitle(BuildContext context) => Text(sender);  @override  Widget buildSubtitle(BuildContext context) => Text(body);}<code_end><topic_end><topic_start>List with spaced itemsPerhaps you want to create a list where all list itemsare spaced evenly, so that the items take up the visible space.For example, the four items in the following image are spaced evenly,with “Item 0” at the top, and “Item 3” at the bottom.At the same time, you might want to allow usersto scroll through the list when the list of items won’t fit,maybe because a device is too small, a user resized a window,or the number of items exceeds the screen size.Typically, you use Spacer to tune the spacing between widgets,or Expanded to expand a widget to fill the available space.However, these solutions are not possible inside scrollable widgets,because they need a finite height constraint.This recipe demonstrates how to use LayoutBuilder and ConstrainedBoxto space out list items evenly when there is enough space, and to allowusers to scroll when there is not enough space,using the following steps:<topic_end><topic_start>1. Add a LayoutBuilder with a SingleChildScrollViewStart by creating a LayoutBuilder. You need to providea builder callback function with two parameters:In this recipe, you won’t be using the BuildContext,but you will need the BoxConstraints in the next step.Inside the builder function, return a SingleChildScrollView.This widget ensures that the child widget can be scrolled,even when the parent container is too small.<code_start>LayoutBuilder(builder: (context, constraints) {  return SingleChildScrollView(    child: Placeholder(),  );});<code_end><topic_end><topic_start>2. Add a ConstrainedBox inside the SingleChildScrollViewIn this step, add a ConstrainedBoxas the child of the SingleChildScrollView.The ConstrainedBox widget imposes additional constraints to its child.Configure the constraint by setting the minHeight parameter to bethe maxHeight of the LayoutBuilder constraints.This ensures that the child widget is constrained to have a minimum height equal to the availablespace provided by the LayoutBuilder constraints,namely the maximum height of the BoxConstraints.<code_start>LayoutBuilder(builder: (context, constraints) {  return SingleChildScrollView(    child: ConstrainedBox(      constraints: BoxConstraints(minHeight: constraints.maxHeight),      child: Placeholder(),    ),  );});<code_end>However, you don’t set the maxHeight parameter,because you need to allow the child to be largerthan the LayoutBuilder size,in case the items don’t fit the screen.<topic_end><topic_start>3. Create a Column with spaced itemsFinally, add a Column as the child of the ConstrainedBox.To space the items evenly, set the mainAxisAlignment to MainAxisAlignment.spaceBetween.<code_start>LayoutBuilder(builder: (context, constraints) {  return SingleChildScrollView(    child: ConstrainedBox(      constraints: BoxConstraints(minHeight: constraints.maxHeight),      child: Column(        mainAxisAlignment: MainAxisAlignment.spaceBetween,        children: [          ItemWidget(text: 'Item 1'),          ItemWidget(text: 'Item 2'),          ItemWidget(text: 'Item 3'),        ],      ),    ),  );});<code_end>Alternatively, you can use the Spacer widget to tune the spacing between the items,or the Expanded widget, if you want one widget to take more space than others.For that, you have to wrap the Column with an IntrinsicHeight widget,which forces the Column widget to size itself to a minimum height,instead of expanding infinitely.<code_start>LayoutBuilder(builder: (context, constraints) {  return SingleChildScrollView(    child: ConstrainedBox(      constraints: BoxConstraints(minHeight: constraints.maxHeight),      child: IntrinsicHeight(        child: Column(          children: [            ItemWidget(text: 'Item 1'),            Spacer(),            ItemWidget(text: 'Item 2'),            Expanded(              child: ItemWidget(text: 'Item 3'),            ),          ],        ),      ),    ),  );});<code_end>lightbulb Tip  Play around with different devices, resizing the app,  or resizing the browser window, and see how the item list adapts  to the available space.<topic_end><topic_start>Interactive exampleThis example shows a list of items that are spaced evenly within a column.The list can be scrolled up and down when the items don’t fit the screen.The number of items is defined by the variable items,change this value to see what happens when the items won’t fit the screen.<code_start>import 'package:flutter/material.dart';void main() => runApp(const SpacedItemsList());class SpacedItemsList extends StatelessWidget {  const SpacedItemsList({super.key});  @override  Widget build(BuildContext context) {    const items = 4;    return MaterialApp(      title: 'Flutter Demo',      debugShowCheckedModeBanner: false,      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),        cardTheme: CardTheme(color: Colors.blue.shade50),        useMaterial3: true,      ),      home: Scaffold(        body: LayoutBuilder(builder: (context, constraints) {          return SingleChildScrollView(            child: ConstrainedBox(              constraints: BoxConstraints(minHeight: constraints.maxHeight),              child: Column(                mainAxisAlignment: MainAxisAlignment.spaceBetween,                crossAxisAlignment: CrossAxisAlignment.stretch,                children: List.generate(                    items, (index) => ItemWidget(text: 'Item $index')),              ),            ),          );        }),      ),    );  }}class ItemWidget extends StatelessWidget {  const ItemWidget({    super.key,    required this.text,  });  final String text;  @override  Widget build(BuildContext context) {    return Card(      child: SizedBox(        height: 100,        child: Center(child: Text(text)),      ),    );  }}<code_end><topic_end><topic_start>Work with long listsThe standard ListView constructor works wellfor small lists. To work with lists that containa large number of items, it’s best to use theListView.builder constructor.In contrast to the default ListView constructor, which requirescreating all items at once, the ListView.builder() constructorcreates items as they’re scrolled onto the screen.<topic_end><topic_start>1. Create a data sourceFirst, you need a data source. For example, your data sourcemight be a list of messages, search results, or products in a store.Most of the time, this data comes from the internet or a database.For this example, generate a list of 10,000 Strings using theList.generate constructor.<code_start>List<String>.generate(10000, (i) => 'Item $i'),<code_end><topic_end><topic_start>2. Convert the data source into widgetsTo display the list of strings, render each String as a widgetusing ListView.builder().In this example, display each String on its own line.<code_start>ListView.builder(  itemCount: items.length,  prototypeItem: ListTile(    title: Text(items.first),  ),  itemBuilder: (context, index) {    return ListTile(      title: Text(items[index]),    );  },)<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() {  runApp(    MyApp(      items: List<String>.generate(10000, (i) => 'Item $i'),    ),  );}class MyApp extends StatelessWidget {  final List<String> items;  const MyApp({super.key, required this.items});  @override  Widget build(BuildContext context) {    const title = 'Long List';    return MaterialApp(      title: title,      home: Scaffold(        appBar: AppBar(          title: const Text(title),        ),        body: ListView.builder(          itemCount: items.length,          prototypeItem: ListTile(            title: Text(items.first),          ),          itemBuilder: (context, index) {            return ListTile(              title: Text(items[index]),            );          },        ),      ),    );  }}<code_end><topic_end><topic_start>Children’s extentTo specify each item’s extent, you can use either itemExtent or prototypeItem.Specifying either is more efficient than letting the children determine their own extentbecause the scrolling machinery can make use of the foreknowledge of the children’sextent to save work, for example when the scroll position changes drastically.<topic_end><topic_start>ScrollingFlutter has many built-in widgets that automaticallyscroll and also offers a variety of widgetsthat you can customize to create specific scrollingbehavior.<topic_end><topic_start>Basic scrollingMany Flutter widgets support scrolling out of the boxand do most of the work for you. For example,SingleChildScrollView automatically scrolls itschild when necessary. Other useful widgets includeListView and GridView.You can check out more of these widgets on thescrolling page of the Widget catalog.<topic_end><topic_start>Infinite scrollingWhen you have a long list of itemsin your ListView or GridView (including an infinite list),you can build the items on demandas they scroll into view. This provides a muchmore performant scrolling experience.For more information, check outListView.builder or GridView.builder.<topic_end><topic_start>Specialized scrollable widgetsThe following widgets provide more specific scrollingbehavior.A video on using DraggableScrollableSheetTurn the scrollable area into a wheel! ListWheelScrollView<topic_end><topic_start>Fancy scrollingPerhaps you want to implement elastic scrolling,also called scroll bouncing. Or maybe you want toimplement other dynamic scrolling effects, like parallax scrolling.Or perhaps you want a scrolling header with very specific behavior,such as shrinking or disappearing.You can achieve all this and more using theFlutter Sliver* classes.A sliver refers to a piece of the scrollable area.You can define and insert a sliver into a CustomScrollViewto have finer-grained control over that area.For more information, check outUsing slivers to achieve fancy scrollingand the Sliver classes.<topic_end><topic_start>Nested scrolling widgetsHow do you nest a scrolling widgetinside another scrolling widgetwithout hurting scrolling performance?Do you set the ShrinkWrap property to true,or do you use a sliver?Check out the “ShrinkWrap vs Slivers” video:<topic_end><topic_start>Using slivers to achieve fancy scrollingA sliver is a portion of a scrollable area that youcan define to behave in a special way.You can use slivers to achieve custom scrolling effects,such as elastic scrolling.For a free, instructor-led video workshop that uses DartPad,check out the following video about using slivers.<topic_end><topic_start>ResourcesFor more information on implementing fancy scrolling effectsin Flutter, see the following resources:<topic_end><topic_start>API docsTo learn more about the available sliver APIs,check out these related API docs:<topic_end><topic_start>Place a floating app bar above a listTo make it easier for users to view a list of items,you might want to hide the app bar as the user scrolls down the list.This is especially true if your app displays a “tall”app bar that occupies a lot of vertical space.Typically, you create an app bar by providing an appBar property to theScaffold widget. This creates a fixed app bar that always remains abovethe body of the Scaffold.Moving the app bar from a Scaffold widget into aCustomScrollView allows you to create an app barthat scrolls offscreen as you scroll through alist of items contained inside the CustomScrollView.This recipe demonstrates how to use a CustomScrollView to display a list ofitems with an app bar on top that scrolls offscreen as the user scrollsdown the list using the following steps:<topic_end><topic_start>1. Create a CustomScrollViewTo create a floating app bar, place the app bar inside aCustomScrollView that also contains the list of items.This synchronizes the scroll position of the app bar and the list of items.You might think of the CustomScrollView widget as a ListViewthat allows you to mix and match different types of scrollable listsand widgets together.The scrollable lists and widgets provided to theCustomScrollView are known as slivers. There are several typesof slivers, such as SliverList, SliverGrid, and SliverAppBar.In fact, the ListView and GridView widgets use the SliverList andSliverGrid widgets to implement scrolling.For this example, create a CustomScrollView that contains aSliverAppBar and a SliverList. In addition, remove any app barsthat you provide to the Scaffold widget.<code_start>Scaffold(  // No appBar property provided, only the body.  body: CustomScrollView(      // Add the app bar and list of items as slivers in the next steps.      slivers: <Widget>[]),);<code_end><topic_end><topic_start>2. Use SliverAppBar to add a floating app barNext, add an app bar to the CustomScrollView.Flutter provides the SliverAppBar widget which,much like the normal AppBar widget,uses the SliverAppBar to display a title,tabs, images and more.However, the SliverAppBar also gives you the ability to create a “floating”app bar that scrolls offscreen as the user scrolls down the list.Furthermore, you can configure the SliverAppBar to shrink andexpand as the user scrolls.To create this effect:<code_start>CustomScrollView(  slivers: [    // Add the app bar to the CustomScrollView.    const SliverAppBar(      // Provide a standard title.      title: Text(title),      // Allows the user to reveal the app bar if they begin scrolling      // back up the list of items.      floating: true,      // Display a placeholder widget to visualize the shrinking size.      flexibleSpace: Placeholder(),      // Make the initial height of the SliverAppBar larger than normal.      expandedHeight: 200,    ),  ],)<code_end>lightbulb Tip  Play around with the  various properties you can pass to the SliverAppBar widget,  and use hot reload to see the results. For example, use an Image  widget for the flexibleSpace property to create a background image that  shrinks in size as it’s scrolled offscreen.<topic_end><topic_start>3. Add a list of items using a SliverListNow that you have the app bar in place, add a list of items to theCustomScrollView. You have two options: a SliverListor a SliverGrid.  If you need to display a list of items one after the other,use the SliverList widget.If you need to display a grid list, use the SliverGrid widget.The SliverList and SliverGrid widgets take one required parameter: aSliverChildDelegate, which provides a listof widgets to SliverList or SliverGrid.For example, the SliverChildBuilderDelegateallows you to create a list of items that are built lazily as you scroll,just like the ListView.builder widget.<code_start>// Next, create a SliverListSliverList(  // Use a delegate to build items as they're scrolled on screen.  delegate: SliverChildBuilderDelegate(    // The builder function returns a ListTile with a title that    // displays the index of the current item.    (context, index) => ListTile(title: Text('Item #$index')),    // Builds 1000 ListTiles    childCount: 1000,  ),)<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const title = 'Floating App Bar';    return MaterialApp(      title: title,      home: Scaffold(        // No appbar provided to the Scaffold, only a body with a        // CustomScrollView.        body: CustomScrollView(          slivers: [            // Add the app bar to the CustomScrollView.            const SliverAppBar(              // Provide a standard title.              title: Text(title),              // Allows the user to reveal the app bar if they begin scrolling              // back up the list of items.              floating: true,              // Display a placeholder widget to visualize the shrinking size.              flexibleSpace: Placeholder(),              // Make the initial height of the SliverAppBar larger than normal.              expandedHeight: 200,            ),            // Next, create a SliverList            SliverList(              // Use a delegate to build items as they're scrolled on screen.              delegate: SliverChildBuilderDelegate(                // The builder function returns a ListTile with a title that                // displays the index of the current item.                (context, index) => ListTile(title: Text('Item #$index')),                // Builds 1000 ListTiles                childCount: 1000,              ),            ),          ],        ),      ),    );  }}<code_end><topic_end><topic_start>Create a scrolling parallax effectWhen you scroll a list of cards (containing images,for example) in an app, you might notice that thoseimages appear to scroll more slowly than the rest of thescreen. It almost looks as if the cards in the listare in the foreground, but the images themselves sitfar off in the distant background. This effect isknown as parallax.In this recipe, you create the parallax effect by buildinga list of cards (with rounded corners containing some text).Each card also contains an image.As the cards slide up the screen,the images within each card slide down.The following animation shows the app’s behavior:<topic_end><topic_start>Create a list to hold the parallax itemsTo display a list of parallax scrolling images,you must first display a list.Create a new stateless widget called ParallaxRecipe.Within ParallaxRecipe, build a widget tree with aSingleChildScrollView and a Column, which formsa list.<code_start>class ParallaxRecipe extends StatelessWidget {  const ParallaxRecipe({super.key});  @override  Widget build(BuildContext context) {    return const SingleChildScrollView(      child: Column(        children: [],      ),    );  }}<code_end><topic_end><topic_start>Display items with text and a static imageEach list item displays a rounded-rectangle backgroundimage, representing one of seven locations in the world.Stacked on top of that background image is thename of the location and its country,positioned in the lower left. Between thebackground image and the text is a dark gradient,which improves the legibilityof the text against the background.Implement a stateless widget called LocationListItemthat consists of the previously mentioned visuals.For now, use a static Image widget for the background.Later, you’ll replace that widget with a parallax version.<code_start>@immutableclass LocationListItem extends StatelessWidget {  const LocationListItem({    super.key,    required this.imageUrl,    required this.name,    required this.country,  });  final String imageUrl;  final String name;  final String country;  @override  Widget build(BuildContext context) {    return Padding(      padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),      child: AspectRatio(        aspectRatio: 16 / 9,        child: ClipRRect(          borderRadius: BorderRadius.circular(16),          child: Stack(            children: [              _buildParallaxBackground(context),              _buildGradient(),              _buildTitleAndSubtitle(),            ],          ),        ),      ),    );  }  Widget _buildParallaxBackground(BuildContext context) {    return Positioned.fill(      child: Image.network(        imageUrl,        fit: BoxFit.cover,      ),    );  }  Widget _buildGradient() {    return Positioned.fill(      child: DecoratedBox(        decoration: BoxDecoration(          gradient: LinearGradient(            colors: [Colors.transparent, Colors.black.withOpacity(0.7)],            begin: Alignment.topCenter,            end: Alignment.bottomCenter,            stops: const [0.6, 0.95],          ),        ),      ),    );  }  Widget _buildTitleAndSubtitle() {    return Positioned(      left: 20,      bottom: 20,      child: Column(        mainAxisSize: MainAxisSize.min,        crossAxisAlignment: CrossAxisAlignment.start,        children: [          Text(            name,            style: const TextStyle(              color: Colors.white,              fontSize: 20,              fontWeight: FontWeight.bold,            ),          ),          Text(            country,            style: const TextStyle(              color: Colors.white,              fontSize: 14,            ),          ),        ],      ),    );  }}<code_end>Next, add the list items to your ParallaxRecipe widget.<code_start>class ParallaxRecipe extends StatelessWidget {  const ParallaxRecipe({super.key});  @override  Widget build(BuildContext context) {    return SingleChildScrollView(      child: Column(        children: [          for (final location in locations)            LocationListItem(              imageUrl: location.imageUrl,              name: location.name,              country: location.place,            ),        ],      ),    );  }}<code_end>You now have a typical, scrollable list of cardsthat displays seven unique locations in the world.In the next step, you add a parallax effect to thebackground image.<topic_end><topic_start>Implement the parallax effectA parallax scrolling effect is achieved by slightlypushing the background image in the opposite directionof the rest of the list. As the list items slide upthe screen, each background image slides slightly downward.Conversely, as the list items slide down the screen,each background image slides slightly upward.Visually, this results in parallax.The parallax effect depends on the list item’scurrent position within its ancestor Scrollable.As the list item’s scroll position changes, the positionof the list item’s background image must also change.This is an interesting problem to solve. The positionof a list item within the Scrollable isn’tavailable until Flutter’s layout phase is complete.This means that the position of the background imagemust be determined in the paint phase, which comes afterthe layout phase. Fortunately, Flutter provides a widgetcalled Flow, which is specifically designed to give youcontrol over the transform of a child widget immediatelybefore the widget is painted. In other words,you can intercept the painting phase and take controlto reposition your child widgets however you want.info Note  To learn more, check out this short  Widget of the Week video on the Flow widget:info Note  In cases where you need control over what a child paints,  rather than where a child is painted,  consider using a CustomPaint widget.In cases where you need control over the layout,  painting, and hit testing, consider defining a  custom RenderBox.Wrap your background Image widget with aFlow widget.<code_start>Widget _buildParallaxBackground(BuildContext context) {  return Flow(    children: [      Image.network(        imageUrl,        fit: BoxFit.cover,      ),    ],  );}<code_end>Introduce a new FlowDelegate called ParallaxFlowDelegate.<code_start>Widget _buildParallaxBackground(BuildContext context) {  return Flow(    delegate: ParallaxFlowDelegate(),    children: [      Image.network(        imageUrl,        fit: BoxFit.cover,      ),    ],  );}<code_end><code_start>class ParallaxFlowDelegate extends FlowDelegate {  ParallaxFlowDelegate();  @override  BoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) {    // TODO: We'll add more to this later.  }  @override  void paintChildren(FlowPaintingContext context) {    // TODO: We'll add more to this later.  }  @override  bool shouldRepaint(covariant FlowDelegate oldDelegate) {    // TODO: We'll add more to this later.    return true;  }}<code_end>A FlowDelegate controls how its children are sizedand where those children are painted. In this case,your Flow widget has only one child: the background image.That image must be exactly as wide as the Flow widget.Return tight width constraints for your background image child.<code_start>@overrideBoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) {  return BoxConstraints.tightFor(    width: constraints.maxWidth,  );}<code_end>Your background images are now sized appropriately,but you still need to calculate the vertical positionof each background image based on its scrollposition, and then paint it.There are three critical pieces of information thatyou need to compute the desired position of abackground image:To look up the bounds of the Scrollable,you pass a ScrollableState into your FlowDelegate.To look up the bounds of your individual list item,pass your list item’s BuildContext into your FlowDelegate.To look up the final size of your background image,assign a GlobalKey to your Image widget,and then you pass that GlobalKey into yourFlowDelegate.Make this information available to ParallaxFlowDelegate.<code_start>@immutableclass LocationListItem extends StatelessWidget {  final GlobalKey _backgroundImageKey = GlobalKey();  Widget _buildParallaxBackground(BuildContext context) {    return Flow(      delegate: ParallaxFlowDelegate(        scrollable: Scrollable.of(context),        listItemContext: context,        backgroundImageKey: _backgroundImageKey,      ),      children: [        Image.network(          imageUrl,          key: _backgroundImageKey,          fit: BoxFit.cover,        ),      ],    );  }}<code_end><code_start>class ParallaxFlowDelegate extends FlowDelegate {  ParallaxFlowDelegate({    required this.scrollable,    required this.listItemContext,    required this.backgroundImageKey,  });  final ScrollableState scrollable;  final BuildContext listItemContext;  final GlobalKey backgroundImageKey;}<code_end>Having all the information needed to implementparallax scrolling, implement the shouldRepaint() method.<code_start>@overridebool shouldRepaint(ParallaxFlowDelegate oldDelegate) {  return scrollable != oldDelegate.scrollable ||      listItemContext != oldDelegate.listItemContext ||      backgroundImageKey != oldDelegate.backgroundImageKey;}<code_end>Now, implement the layout calculations for the parallax effect.First, calculate the pixel position of a listitem within its ancestor Scrollable.<code_start>@overridevoid paintChildren(FlowPaintingContext context) {  // Calculate the position of this list item within the viewport.  final scrollableBox = scrollable.context.findRenderObject() as RenderBox;  final listItemBox = listItemContext.findRenderObject() as RenderBox;  final listItemOffset = listItemBox.localToGlobal(      listItemBox.size.centerLeft(Offset.zero),      ancestor: scrollableBox);}<code_end>Use the pixel position of the list item to calculate itspercentage from the top of the Scrollable.A list item at the top of the scrollable area shouldproduce 0%, and a list item at the bottom of thescrollable area should produce 100%.<code_start>@overridevoid paintChildren(FlowPaintingContext context) {  // Calculate the position of this list item within the viewport.  final scrollableBox = scrollable.context.findRenderObject() as RenderBox;  final listItemBox = listItemContext.findRenderObject() as RenderBox;  final listItemOffset = listItemBox.localToGlobal(      listItemBox.size.centerLeft(Offset.zero),      ancestor: scrollableBox);  // Determine the percent position of this list item within the  // scrollable area.  final viewportDimension = scrollable.position.viewportDimension;  final scrollFraction =      (listItemOffset.dy / viewportDimension).clamp(0.0, 1.0);<code_end>Use the scroll percentage to calculate an Alignment.At 0%, you want Alignment(0.0, -1.0),and at 100%, you want Alignment(0.0, 1.0).These coordinates correspond to top and bottomalignment, respectively.<code_start>@overridevoid paintChildren(FlowPaintingContext context) {  // Calculate the position of this list item within the viewport.  final scrollableBox = scrollable.context.findRenderObject() as RenderBox;  final listItemBox = listItemContext.findRenderObject() as RenderBox;  final listItemOffset = listItemBox.localToGlobal(      listItemBox.size.centerLeft(Offset.zero),      ancestor: scrollableBox);  // Determine the percent position of this list item within the  // scrollable area.  final viewportDimension = scrollable.position.viewportDimension;  final scrollFraction =      (listItemOffset.dy / viewportDimension).clamp(0.0, 1.0);  // Calculate the vertical alignment of the background  // based on the scroll percent.  final verticalAlignment = Alignment(0.0, scrollFraction * 2 - 1);<code_end>Use verticalAlignment, along with the size of thelist item and the size of the background image,to produce a Rect that determines where thebackground image should be positioned.<code_start>@overridevoid paintChildren(FlowPaintingContext context) {  // Calculate the position of this list item within the viewport.  final scrollableBox = scrollable.context.findRenderObject() as RenderBox;  final listItemBox = listItemContext.findRenderObject() as RenderBox;  final listItemOffset = listItemBox.localToGlobal(      listItemBox.size.centerLeft(Offset.zero),      ancestor: scrollableBox);  // Determine the percent position of this list item within the  // scrollable area.  final viewportDimension = scrollable.position.viewportDimension;  final scrollFraction =      (listItemOffset.dy / viewportDimension).clamp(0.0, 1.0);  // Calculate the vertical alignment of the background  // based on the scroll percent.  final verticalAlignment = Alignment(0.0, scrollFraction * 2 - 1);  // Convert the background alignment into a pixel offset for  // painting purposes.  final backgroundSize =      (backgroundImageKey.currentContext!.findRenderObject() as RenderBox)          .size;  final listItemSize = context.size;  final childRect =      verticalAlignment.inscribe(backgroundSize, Offset.zero & listItemSize);<code_end>Using childRect, paint the background image withthe desired translation transformation.It’s this transformation over time that gives you theparallax effect.<code_start>@overridevoid paintChildren(FlowPaintingContext context) {  // Calculate the position of this list item within the viewport.  final scrollableBox = scrollable.context.findRenderObject() as RenderBox;  final listItemBox = listItemContext.findRenderObject() as RenderBox;  final listItemOffset = listItemBox.localToGlobal(      listItemBox.size.centerLeft(Offset.zero),      ancestor: scrollableBox);  // Determine the percent position of this list item within the  // scrollable area.  final viewportDimension = scrollable.position.viewportDimension;  final scrollFraction =      (listItemOffset.dy / viewportDimension).clamp(0.0, 1.0);  // Calculate the vertical alignment of the background  // based on the scroll percent.  final verticalAlignment = Alignment(0.0, scrollFraction * 2 - 1);  // Convert the background alignment into a pixel offset for  // painting purposes.  final backgroundSize =      (backgroundImageKey.currentContext!.findRenderObject() as RenderBox)          .size;  final listItemSize = context.size;  final childRect =      verticalAlignment.inscribe(backgroundSize, Offset.zero & listItemSize);  // Paint the background.  context.paintChild(    0,    transform:        Transform.translate(offset: Offset(0.0, childRect.top)).transform,  );<code_end>You need one final detail to achieve the parallax effect.The ParallaxFlowDelegate repaints when the inputs change,but the ParallaxFlowDelegate doesn’t repaint every timethe scroll position changes.Pass the ScrollableState’s ScrollPosition tothe FlowDelegate superclass so that the FlowDelegaterepaints every time the ScrollPosition changes.<code_start>class ParallaxFlowDelegate extends FlowDelegate {  ParallaxFlowDelegate({    required this.scrollable,    required this.listItemContext,    required this.backgroundImageKey,  }) : super(repaint: scrollable.position);}<code_end>Congratulations!You now have a list of cards with parallax,scrolling background images.<topic_end><topic_start>Interactive exampleRun the app:<code_start>import 'package:flutter/material.dart';import 'package:flutter/rendering.dart';const Color darkBlue = Color.fromARGB(255, 18, 32, 47);void main() {  runApp(const MyApp());}class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),      debugShowCheckedModeBanner: false,      home: const Scaffold(        body: Center(          child: ExampleParallax(),        ),      ),    );  }}class ExampleParallax extends StatelessWidget {  const ExampleParallax({    super.key,  });  @override  Widget build(BuildContext context) {    return SingleChildScrollView(      child: Column(        children: [          for (final location in locations)            LocationListItem(              imageUrl: location.imageUrl,              name: location.name,              country: location.place,            ),        ],      ),    );  }}class LocationListItem extends StatelessWidget {  LocationListItem({    super.key,    required this.imageUrl,    required this.name,    required this.country,  });  final String imageUrl;  final String name;  final String country;  final GlobalKey _backgroundImageKey = GlobalKey();  @override  Widget build(BuildContext context) {    return Padding(      padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),      child: AspectRatio(        aspectRatio: 16 / 9,        child: ClipRRect(          borderRadius: BorderRadius.circular(16),          child: Stack(            children: [              _buildParallaxBackground(context),              _buildGradient(),              _buildTitleAndSubtitle(),            ],          ),        ),      ),    );  }  Widget _buildParallaxBackground(BuildContext context) {    return Flow(      delegate: ParallaxFlowDelegate(        scrollable: Scrollable.of(context),        listItemContext: context,        backgroundImageKey: _backgroundImageKey,      ),      children: [        Image.network(          imageUrl,          key: _backgroundImageKey,          fit: BoxFit.cover,        ),      ],    );  }  Widget _buildGradient() {    return Positioned.fill(      child: DecoratedBox(        decoration: BoxDecoration(          gradient: LinearGradient(            colors: [Colors.transparent, Colors.black.withOpacity(0.7)],            begin: Alignment.topCenter,            end: Alignment.bottomCenter,            stops: const [0.6, 0.95],          ),        ),      ),    );  }  Widget _buildTitleAndSubtitle() {    return Positioned(      left: 20,      bottom: 20,      child: Column(        mainAxisSize: MainAxisSize.min,        crossAxisAlignment: CrossAxisAlignment.start,        children: [          Text(            name,            style: const TextStyle(              color: Colors.white,              fontSize: 20,              fontWeight: FontWeight.bold,            ),          ),          Text(            country,            style: const TextStyle(              color: Colors.white,              fontSize: 14,            ),          ),        ],      ),    );  }}class ParallaxFlowDelegate extends FlowDelegate {  ParallaxFlowDelegate({    required this.scrollable,    required this.listItemContext,    required this.backgroundImageKey,  }) : super(repaint: scrollable.position);  final ScrollableState scrollable;  final BuildContext listItemContext;  final GlobalKey backgroundImageKey;  @override  BoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) {    return BoxConstraints.tightFor(      width: constraints.maxWidth,    );  }  @override  void paintChildren(FlowPaintingContext context) {    // Calculate the position of this list item within the viewport.    final scrollableBox = scrollable.context.findRenderObject() as RenderBox;    final listItemBox = listItemContext.findRenderObject() as RenderBox;    final listItemOffset = listItemBox.localToGlobal(        listItemBox.size.centerLeft(Offset.zero),        ancestor: scrollableBox);    // Determine the percent position of this list item within the    // scrollable area.    final viewportDimension = scrollable.position.viewportDimension;    final scrollFraction =        (listItemOffset.dy / viewportDimension).clamp(0.0, 1.0);    // Calculate the vertical alignment of the background    // based on the scroll percent.    final verticalAlignment = Alignment(0.0, scrollFraction * 2 - 1);    // Convert the background alignment into a pixel offset for    // painting purposes.    final backgroundSize =        (backgroundImageKey.currentContext!.findRenderObject() as RenderBox)            .size;    final listItemSize = context.size;    final childRect =        verticalAlignment.inscribe(backgroundSize, Offset.zero & listItemSize);    // Paint the background.    context.paintChild(      0,      transform:          Transform.translate(offset: Offset(0.0, childRect.top)).transform,    );  }  @override  bool shouldRepaint(ParallaxFlowDelegate oldDelegate) {    return scrollable != oldDelegate.scrollable ||        listItemContext != oldDelegate.listItemContext ||        backgroundImageKey != oldDelegate.backgroundImageKey;  }}class Parallax extends SingleChildRenderObjectWidget {  const Parallax({    super.key,    required Widget background,  }) : super(child: background);  @override  RenderObject createRenderObject(BuildContext context) {    return RenderParallax(scrollable: Scrollable.of(context));  }  @override  void updateRenderObject(      BuildContext context, covariant RenderParallax renderObject) {    renderObject.scrollable = Scrollable.of(context);  }}class ParallaxParentData extends ContainerBoxParentData<RenderBox> {}class RenderParallax extends RenderBox    with RenderObjectWithChildMixin<RenderBox>, RenderProxyBoxMixin {  RenderParallax({    required ScrollableState scrollable,  }) : _scrollable = scrollable;  ScrollableState _scrollable;  ScrollableState get scrollable => _scrollable;  set scrollable(ScrollableState value) {    if (value != _scrollable) {      if (attached) {        _scrollable.position.removeListener(markNeedsLayout);      }      _scrollable = value;      if (attached) {        _scrollable.position.addListener(markNeedsLayout);      }    }  }  @override  void attach(covariant PipelineOwner owner) {    super.attach(owner);    _scrollable.position.addListener(markNeedsLayout);  }  @override  void detach() {    _scrollable.position.removeListener(markNeedsLayout);    super.detach();  }  @override  void setupParentData(covariant RenderObject child) {    if (child.parentData is! ParallaxParentData) {      child.parentData = ParallaxParentData();    }  }  @override  void performLayout() {    size = constraints.biggest;    // Force the background to take up all available width    // and then scale its height based on the image's aspect ratio.    final background = child!;    final backgroundImageConstraints =        BoxConstraints.tightFor(width: size.width);    background.layout(backgroundImageConstraints, parentUsesSize: true);    // Set the background's local offset, which is zero.    (background.parentData as ParallaxParentData).offset = Offset.zero;  }  @override  void paint(PaintingContext context, Offset offset) {    // Get the size of the scrollable area.    final viewportDimension = scrollable.position.viewportDimension;    // Calculate the global position of this list item.    final scrollableBox = scrollable.context.findRenderObject() as RenderBox;    final backgroundOffset =        localToGlobal(size.centerLeft(Offset.zero), ancestor: scrollableBox);    // Determine the percent position of this list item within the    // scrollable area.    final scrollFraction =        (backgroundOffset.dy / viewportDimension).clamp(0.0, 1.0);    // Calculate the vertical alignment of the background    // based on the scroll percent.    final verticalAlignment = Alignment(0.0, scrollFraction * 2 - 1);    // Convert the background alignment into a pixel offset for    // painting purposes.    final background = child!;    final backgroundSize = background.size;    final listItemSize = size;    final childRect =        verticalAlignment.inscribe(backgroundSize, Offset.zero & listItemSize);    // Paint the background.    context.paintChild(        background,        (background.parentData as ParallaxParentData).offset +            offset +            Offset(0.0, childRect.top));  }}class Location {  const Location({    required this.name,    required this.place,    required this.imageUrl,  });  final String name;  final String place;  final String imageUrl;}const urlPrefix =    'https://docs.flutter.dev/cookbook/img-files/effects/parallax';const locations = [  Location(    name: 'Mount Rushmore',    place: 'U.S.A',    imageUrl: '$urlPrefix/01-mount-rushmore.jpg',  ),  Location(    name: 'Gardens By The Bay',    place: 'Singapore',    imageUrl: '$urlPrefix/02-singapore.jpg',  ),  Location(    name: 'Machu Picchu',    place: 'Peru',    imageUrl: '$urlPrefix/03-machu-picchu.jpg',  ),  Location(    name: 'Vitznau',    place: 'Switzerland',    imageUrl: '$urlPrefix/04-vitznau.jpg',  ),  Location(    name: 'Bali',    place: 'Indonesia',    imageUrl: '$urlPrefix/05-bali.jpg',  ),  Location(    name: 'Mexico City',    place: 'Mexico',    imageUrl: '$urlPrefix/06-mexico-city.jpg',  ),  Location(    name: 'Cairo',    place: 'Egypt',    imageUrl: '$urlPrefix/07-cairo.jpg',  ),];<code_end><topic_end><topic_start>Creating responsive and adaptive appsOne of Flutter’s primary goals is to create a frameworkthat allows you to develop apps from a single codebasethat look and feel great on any platform.This means that your app might appear on screens ofmany different sizes, from a watch, to a foldablephone with two screens, to a high def monitor.Two terms that describe concepts for thisscenario are adaptive and responsive. Ideally,you’d want your app to be both but what,exactly, does this mean?These terms are similar, but they are not the same.<topic_end><topic_start>The difference between an adaptive and a responsive appAdaptive and responsive can be viewed as separatedimensions of an app: you can have an adaptive appthat is not responsive, or vice versa. And, of course,an app can be both, or neither.Learn more in the following 5-minute video:Adaptive vs Responsive<topic_end><topic_start>Creating a responsive Flutter appFlutter allows you to create apps that self-adaptto the device’s screen size and orientation.There are two basic approaches to creating Flutterapps with responsive design:Other useful widgets and classes for creating a responsive UI:<topic_end><topic_start>Other resourcesFor more information, here are a few resources,including contributions from the Flutter community:<topic_end><topic_start>Creating an adaptive Flutter appLearn more about creating an adaptive Flutter app withBuilding adaptive apps, written by the gskinner team.You might also check out the following episodesof The Boring Show:Adaptive layoutsAdaptive layouts, part 2For an excellent example of an adaptive app,check out Flutter Folio, a scrapbooking app createdin collaboration with gskinner and the Flutter team:The Folio source code is also available on GitHub.Learn more on the gskinner blog.<topic_end><topic_start>Other resourcesYou can learn more about creating platform adaptive appsin the following resources:<topic_end><topic_start>Creating responsive and adaptive appsOne of Flutter’s primary goals is to create a frameworkthat allows you to develop apps from a single codebasethat look and feel great on any platform.This means that your app might appear on screens ofmany different sizes, from a watch, to a foldablephone with two screens, to a high def monitor.Two terms that describe concepts for thisscenario are adaptive and responsive. Ideally,you’d want your app to be both but what,exactly, does this mean?These terms are similar, but they are not the same.<topic_end><topic_start>The difference between an adaptive and a responsive appAdaptive and responsive can be viewed as separatedimensions of an app: you can have an adaptive appthat is not responsive, or vice versa. And, of course,an app can be both, or neither.Learn more in the following 5-minute video:Adaptive vs Responsive<topic_end><topic_start>Creating a responsive Flutter appFlutter allows you to create apps that self-adaptto the device’s screen size and orientation.There are two basic approaches to creating Flutterapps with responsive design:Other useful widgets and classes for creating a responsive UI:<topic_end><topic_start>Other resourcesFor more information, here are a few resources,including contributions from the Flutter community:<topic_end><topic_start>Creating an adaptive Flutter appLearn more about creating an adaptive Flutter app withBuilding adaptive apps, written by the gskinner team.You might also check out the following episodesof The Boring Show:Adaptive layoutsAdaptive layouts, part 2For an excellent example of an adaptive app,check out Flutter Folio, a scrapbooking app createdin collaboration with gskinner and the Flutter team:The Folio source code is also available on GitHub.Learn more on the gskinner blog.<topic_end><topic_start>Other resourcesYou can learn more about creating platform adaptive appsin the following resources:<topic_end><topic_start>Building adaptive apps<topic_end><topic_start>OverviewFlutter provides new opportunities to build apps that canrun on mobile, desktop, and the web from a single codebase.However, with these opportunities, come new challenges.You want your app to feel familiar to users,adapting to each platform by maximizing usability andensuring a comfortable and seamless experience.That is, you need to build apps that are not justmultiplatform, but are fully platform adaptive.There are many considerations for developing platform-adaptiveapps, but they fall into three major categories:This page covers all three categories in detailusing code snippets to illustrate the concepts.If you’d like to see how these concepts come together,check out the Flokk and Folio examples thatwere built using the concepts described here.Original demo code for adaptive app development techniques from flutter-adaptive-demo.<topic_end><topic_start>Building adaptive layoutsOne of the first things you must consider when writingyour app for multiple platforms is how to adaptit to the various sizes and shapes of the screens thatit will run on.<topic_end><topic_start>Layout widgetsIf you’ve been building apps or websites,you’re probably familiar with creating responsive interfaces.Luckily for Flutter developers,there are a large set of widgets to make this easier.Some of Flutter’s most useful layout widgets include:Single childAlign—Aligns a child within itself.It takes a double value between -1 and 1,for both the vertical and horizontal alignment.AspectRatio—Attempts to size thechild to a specific aspect ratio.ConstrainedBox—Imposes size constraints on its child,offering control over the minimum or maximum size.CustomSingleChildLayout—Uses a delegate functionto position a single child. The delegate can determinethe layout constraints and positioning for the child.Expanded and Flexible—Allows a child of aRow or Column to shrink or grow to fill any available space.FractionallySizedBox—Sizes its child to a fractionof the available space.LayoutBuilder—Builds a widget that can reflowitself based on its parents size.SingleChildScrollView—Adds scrolling to a single child.Often used with a Row or Column.MultichildColumn, Row, and Flex—Lays out childrenin a single horizontal or vertical run.Both Column and Row extend the Flex widget.CustomMultiChildLayout—Uses a delegate function toposition multiple children during the layout phase.Flow—Similar to CustomMultiChildLayout,but more efficient because it’s performed during thepaint phase rather than the layout phase.ListView, GridView, andCustomScrollView—Provides scrollablelists of children.Stack—Layers and positions multiple childrenrelative to the edges of the Stack.Functions similarly to position-fixed in CSS.Table—Uses a classic table layout algorithm forits children, combining multiple rows and columns.Wrap—Displays its children in multiple horizontalor vertical runs.To see more available widgets and example code, seeLayout widgets.<topic_end><topic_start>Visual densityDifferent input devices offer various levels of precision,which necessitate differently sized hit areas.Flutter’s VisualDensity class makes it easy to adjust thedensity of your views across the entire application,for example, by making a button larger(and therefore easier to tap) on a touch device.When you change the VisualDensity for your MaterialApp,MaterialComponents that support it animate their densitiesto match. By default, both horizontal and vertical densitiesare set to 0.0, but you can set the densities to any negativeor positive value that you want. By switching between differentdensities, you can easily adjust your UI:To set a custom visual density, inject the density intoyour MaterialApp theme:<code_start>double densityAmt = touchMode ? 0.0 : -1.0;VisualDensity density =    VisualDensity(horizontal: densityAmt, vertical: densityAmt);return MaterialApp(  theme: ThemeData(visualDensity: density),  home: MainAppScaffold(),  debugShowCheckedModeBanner: false,);<code_end>To use VisualDensity inside your own views,you can look it up:<code_start>VisualDensity density = Theme.of(context).visualDensity;<code_end>Not only does the container react automatically to changesin density, it also animates when it changes.This ties together your custom components,along with the built-in components,for a smooth transition effect across the app.As shown, VisualDensity is unit-less,so it can mean different things to different views.In this example, 1 density unit equals 6 pixels,but this is totally up to your views to decide.The fact that it is unit-less makes it quite versatile,and it should work in most contexts.It’s worth noting that the Material Components generallyuse a value of around 4 logical pixels for eachvisual density unit. For more information about thesupported components, see VisualDensity API.For more information about density principles in general,see the Material Design guide.<topic_end><topic_start>Contextual layoutIf you need more than density changes and can’t find awidget that does what you need, you can take a moreprocedural approach to adjust parameters, calculate sizes,swap widgets, or completely restructure your UI to suita particular form factor.<topic_end><topic_start>Screen-based breakpointsThe simplest form of procedural layouts usesscreen-based breakpoints. In Flutter,this can be done with the MediaQuery API.There are no hard and fast rules for the sizes to usehere, but these are general values:<code_start>class FormFactor {  static double desktop = 900;  static double tablet = 600;  static double handset = 300;}<code_end>Using breakpoints, you can set up a simple systemto determine the device type:<code_start>ScreenType getFormFactor(BuildContext context) {  // Use .shortestSide to detect device type regardless of orientation  double deviceWidth = MediaQuery.of(context).size.shortestSide;  if (deviceWidth > FormFactor.desktop) return ScreenType.desktop;  if (deviceWidth > FormFactor.tablet) return ScreenType.tablet;  if (deviceWidth > FormFactor.handset) return ScreenType.handset;  return ScreenType.watch;}<code_end>As an alternative, you could abstract it moreand define it in terms of small to large:<code_start>enum ScreenSize { small, normal, large, extraLarge }ScreenSize getSize(BuildContext context) {  double deviceWidth = MediaQuery.of(context).size.shortestSide;  if (deviceWidth > 900) return ScreenSize.extraLarge;  if (deviceWidth > 600) return ScreenSize.large;  if (deviceWidth > 300) return ScreenSize.normal;  return ScreenSize.small;}<code_end>Screen-based breakpoints are best used for makingtop-level decisions in your app. Changing things likevisual density, paddings, or font-sizes are best whendefined on a global basis.You can also use screen-based breakpoints to reflow yourtop-level widget trees. For example, you could switchfrom a vertical to a horizontal layout when the user isn’t on a handset:<code_start>bool isHandset = MediaQuery.of(context).size.width < 600;return Flex(  direction: isHandset ? Axis.vertical : Axis.horizontal,  children: const [Text('Foo'), Text('Bar'), Text('Baz')],);<code_end>In another widget,you might swap some of the children completely:<code_start>Widget foo = Row(  children: [    ...isHandset ? _getHandsetChildren() : _getNormalChildren(),  ],);<code_end><topic_end><topic_start>Use LayoutBuilder for extra flexibilityEven though checking total screen size is great forfull-screen pages or making global layout decisions,it’s often not ideal for nested subviews.Often, subviews have their own internal breakpointsand care only about the space that they have available to render.The simplest way to handle this in Flutter is using theLayoutBuilder class. LayoutBuilder allows awidget to respond to incoming local size constraints,which can make the widget more versatile than if itdepended on a global value.The previous example could be rewritten using LayoutBuilder:<code_start>Widget foo = LayoutBuilder(builder: (context, constraints) {  bool useVerticalLayout = constraints.maxWidth < 400;  return Flex(    direction: useVerticalLayout ? Axis.vertical : Axis.horizontal,    children: const [      Text('Hello'),      Text('World'),    ],  );});<code_end>This widget can now be composed within a side panel,dialog, or even a full-screen view,and adapt its layout to whatever space is provided.<topic_end><topic_start>Device segmentationThere are times when you want to make layout decisionsbased on the actual platform you’re running on,regardless of size. For example, when building acustom title bar, you might need to check the operatingsystem type and tweak the layout of your title bar,so it doesn’t get covered by the native window buttons.To determine which combination of platforms you’re on,you can use the Platform API along with the kIsWeb value:<code_start>bool get isMobileDevice => !kIsWeb && (Platform.isIOS || Platform.isAndroid);bool get isDesktopDevice =>    !kIsWeb && (Platform.isMacOS || Platform.isWindows || Platform.isLinux);bool get isMobileDeviceOrWeb => kIsWeb || isMobileDevice;bool get isDesktopDeviceOrWeb => kIsWeb || isDesktopDevice;<code_end>The Platform API can’t be accessed from web builds withoutthrowing an exception, because the dart.io package isn’tsupported on the web target. As a result, the above code checksfor web first, and because of short-circuiting,Dart never calls Platform on web targets.Use Platform/kIsWeb when the logic absolutely mustrun for a given platform. For example,talking to a plugin that only works on iOS,or displaying a widget that only conforms toPlay Store policy and not the App Store’s.<topic_end><topic_start>Single source of truth for stylingYou’ll probably find it easier to maintain your viewsif you create a single source of truth for styling valueslike padding, spacing, corner shape, font sizes, and so on.This can be done easily with some helper classes:<code_start>class Insets {  static const double xsmall = 3;  static const double small = 4;  static const double medium = 5;  static const double large = 10;  static const double extraLarge = 20;  // etc}class Fonts {  static const String raleway = 'Raleway';  // etc}class TextStyles {  static const TextStyle raleway = TextStyle(    fontFamily: Fonts.raleway,  );  static TextStyle buttonText1 =      const TextStyle(fontWeight: FontWeight.bold, fontSize: 14);  static TextStyle buttonText2 =      const TextStyle(fontWeight: FontWeight.normal, fontSize: 11);  static TextStyle h1 =      const TextStyle(fontWeight: FontWeight.bold, fontSize: 22);  static TextStyle h2 =      const TextStyle(fontWeight: FontWeight.bold, fontSize: 16);  static TextStyle body1 = raleway.copyWith(color: const Color(0xFF42A5F5));  // etc}<code_end>These constants can then be used in place of hard-coded numeric values:<code_start>return Padding(  padding: const EdgeInsets.all(Insets.small),  child: Text('Hello!', style: TextStyles.body1),);<code_end>Use Theme.of(context).platform for theming anddesign choices, like what kind of switches to showand general Cupertino/Material adaptions.With all views referencing the same shared-design system rules,they tend to look better and more consistent.Making a change or adjusting a value for a specific platformcan be done in a single place, instead of using an error-pronesearch and replace. Using shared rules has the added benefitof helping enforce consistency on the design side.Some common design system categories that can be representedthis way are:Like most rules, there are exceptions:one-off values that are used nowhere else in the app.There is little point in cluttering up the styling ruleswith these values, but it’s worth considering if theyshould be derived from an existing value (for example,padding + 1.0). You should also watch for reuse or duplicationof the same semantic values. Those values should likely beadded to the global styling ruleset.<topic_end><topic_start>Design to the strengths of each form factorBeyond screen size, you should also spend timeconsidering the unique strengths and weaknessesof different form factors. It isn’t always idealfor your multiplatform app to offer identicalfunctionality everywhere. Consider whether it makessense to focus on specific capabilities,or even remove certain features, on some device categories.For example, mobile devices are portable and have cameras,but they aren’t well suited for detailed creative work.With this in mind, you might focus more on capturing contentand tagging it with location data for a mobile UI,but focus on organizing or manipulating that contentfor a tablet or desktop UI.Another example is leveraging the web’s extremely low barrierfor sharing. If you’re deploying a web app,decide which deep links to support,and design your navigation routes with those in mind.The key takeaway here is to think about what eachplatform does best and see if there are unique capabilitiesyou can leverage.<topic_end><topic_start>Use desktop build targets for rapid testingOne of the most effective ways to test adaptiveinterfaces is to take advantage of the desktop build targets.When running on a desktop, you can easily resize the windowwhile the app is running to preview various screen sizes.This, combined with hot reload, can greatly accelerate thedevelopment of a responsive UI.<topic_end><topic_start>Solve touch firstBuilding a great touch UI can often be more difficultthan a traditional desktop UI due, in part,to the lack of input accelerators like right-click,scroll wheel, or keyboard shortcuts.One way to approach this challenge is to focus initiallyon a great touch-oriented UI. You can still do most ofyour testing using the desktop target for its iteration speed.But, remember to switch frequently to a mobile device toverify that everything feels right.After you have the touch interface polished, you can tweakthe visual density for mouse users, and then layer on allthe additional inputs. Approach these other inputs asaccelerator—alternatives that make a task faster.The important thing to consider is what a user expectswhen using a particular input device,and work to reflect that in your app.<topic_end><topic_start>InputOf course, it isn’t enough to just adapt how your app looks,you also have to support varying user inputs.The mouse and keyboard introduce input types beyond thosefound on a touch device—like scroll wheel, right-click,hover interactions, tab traversal, and keyboard shortcuts.<topic_end><topic_start>Scroll wheelScrolling widgets like ScrollView or ListViewsupport the scroll wheel by default, and becausealmost every scrollable custom widget is builtusing one of these, it works with them as well.If you need to implement custom scroll behavior,you can use the Listener widget, which lets youcustomize how your UI reacts to the scroll wheel.<code_start>return Listener(  onPointerSignal: (event) {    if (event is PointerScrollEvent) print(event.scrollDelta.dy);  },  child: ListView(),);<code_end><topic_end><topic_start>Tab traversal and focus interactionsUsers with physical keyboards expect that they can usethe tab key to quickly navigate your application,and users with motor or vision differences often relycompletely on keyboard navigation.There are two considerations for tab interactions:how focus moves from widget to widget, known as traversal,and the visual highlight shown when a widget is focused.Most built-in components, like buttons and text fields,support traversal and highlights by default.If you have your own widget that you want included intraversal, you can use the FocusableActionDetector widgetto create your own controls. It combines the functionalityof Actions, Shortcuts, MouseRegion, andFocus widgets to create a detector that defines actionsand key bindings, and provides callbacks for handling focusand hover highlights.<code_start>class _BasicActionDetectorState extends State<BasicActionDetector> {  bool _hasFocus = false;  @override  Widget build(BuildContext context) {    return FocusableActionDetector(      onFocusChange: (value) => setState(() => _hasFocus = value),      actions: <Type, Action<Intent>>{        ActivateIntent: CallbackAction<Intent>(onInvoke: (intent) {          print('Enter or Space was pressed!');          return null;        }),      },      child: Stack(        clipBehavior: Clip.none,        children: [          const FlutterLogo(size: 100),          // Position focus in the negative margin for a cool effect          if (_hasFocus)            Positioned(              left: -4,              top: -4,              bottom: -4,              right: -4,              child: _roundedBorder(),            )        ],      ),    );  }}<code_end><topic_end><topic_start>Controlling traversal orderTo get more control over the order thatwidgets are focused on when the user presses tab,you can use FocusTraversalGroup to define sectionsof the tree that should be treated as a group when tabbing.For example, you might to tab through all the fields ina form before tabbing to the submit button:<code_start>return Column(children: [  FocusTraversalGroup(    child: MyFormWithMultipleColumnsAndRows(),  ),  SubmitButton(),]);<code_end>Flutter has several built-in ways to traverse widgets and groups,defaulting to the ReadingOrderTraversalPolicy class.This class usually works well, but it’s possible to modify thisusing another predefined TraversalPolicy class or by creatinga custom policy.<topic_end><topic_start>Keyboard acceleratorsIn addition to tab traversal, desktop and web users are accustomedto having various keyboard shortcuts bound to specific actions.Whether it’s the Delete key for quick deletions orControl+N for a new document, be sure to consider the differentaccelerators your users expect. The keyboard is a powerfulinput tool, so try to squeeze as much efficiency from it as you can.Your users will appreciate it!Keyboard accelerators can be accomplished in a few ways in Flutterdepending on your goals.If you have a single widget like a TextField or a Button thatalready has a focus node, you can wrap it in a KeyboardListeneror a Focus widget and listen for keyboard events:<code_start>  @override  Widget build(BuildContext context) {    return Focus(      onKeyEvent: (node, event) {        if (event is KeyDownEvent) {          print(event.logicalKey);        }        return KeyEventResult.ignored;      },      child: ConstrainedBox(        constraints: const BoxConstraints(maxWidth: 400),        child: const TextField(          decoration: InputDecoration(            border: OutlineInputBorder(),          ),        ),      ),    );  }}<code_end>If you’d like to apply a set of keyboard shortcuts to alarge section of the tree, you can use the Shortcuts widget:<code_start>// Define a class for each type of shortcut action you wantclass CreateNewItemIntent extends Intent {  const CreateNewItemIntent();}Widget build(BuildContext context) {  return Shortcuts(    // Bind intents to key combinations    shortcuts: const <ShortcutActivator, Intent>{      SingleActivator(LogicalKeyboardKey.keyN, control: true):          CreateNewItemIntent(),    },    child: Actions(      // Bind intents to an actual method in your code      actions: <Type, Action<Intent>>{        CreateNewItemIntent: CallbackAction<CreateNewItemIntent>(          onInvoke: (intent) => _createNewItem(),        ),      },      // Your sub-tree must be wrapped in a focusNode, so it can take focus.      child: Focus(        autofocus: true,        child: Container(),      ),    ),  );}<code_end>The Shortcuts widget is useful because it onlyallows shortcuts to be fired when this widget treeor one of its children has focus and is visible.The final option is a global listener. This listenercan be used for always-on, app-wide shortcuts or forpanels that can accept shortcuts whenever they’re visible(regardless of their focus state). Adding global listenersis easy with HardwareKeyboard:<code_start>@overridevoid initState() {  super.initState();  HardwareKeyboard.instance.addHandler(_handleKey);}@overridevoid dispose() {  HardwareKeyboard.instance.removeHandler(_handleKey);  super.dispose();}<code_end>To check key combinations with the global listener,you can use the HardwareKeyboard.instance.logicalKeysPressed set.For example, a method like the following can check whether anyof the provided keys are being held down:<code_start>static bool isKeyDown(Set<LogicalKeyboardKey> keys) {  return keys      .intersection(HardwareKeyboard.instance.logicalKeysPressed)      .isNotEmpty;}<code_end>Putting these two things together,you can fire an action when Shift+N is pressed:<code_start>bool _handleKey(KeyEvent event) {  bool isShiftDown = isKeyDown({    LogicalKeyboardKey.shiftLeft,    LogicalKeyboardKey.shiftRight,  });  if (isShiftDown && event.logicalKey == LogicalKeyboardKey.keyN) {    _createNewItem();    return true;  }  return false;}<code_end>One note of caution when using the static listener,is that you often need to disable it when the useris typing in a field or when the widget it’s associated withis hidden from view.Unlike with Shortcuts or KeyboardListener,this is your responsibility to manage. This can be especiallyimportant when you’re binding a Delete/Backspace accelerator forDelete, but then have child TextFields that the usermight be typing in.<topic_end><topic_start>Mouse enter, exit, and hoverOn desktop, it’s common to change the mouse cursorto indicate the functionality about the content themouse is hovering over. For example, you usually seea hand cursor when you hover over a button,or an I cursor when you hover over text.The Material Component set has built-in supportfor your standard button and text cursors.To change the cursor from within your own widgets,use MouseRegion:<code_start>// Show hand cursorreturn MouseRegion(  cursor: SystemMouseCursors.click,  // Request focus when clicked  child: GestureDetector(    onTap: () {      Focus.of(context).requestFocus();      _submit();    },    child: Logo(showBorder: hasFocus),  ),);<code_end>MouseRegion is also useful for creating customrollover and hover effects:<code_start>return MouseRegion(  onEnter: (_) => setState(() => _isMouseOver = true),  onExit: (_) => setState(() => _isMouseOver = false),  onHover: (e) => print(e.localPosition),  child: Container(    height: 500,    color: _isMouseOver ? Colors.blue : Colors.black,  ),);<code_end><topic_end><topic_start>Idioms and normsThe final area to consider for adaptive apps is platform standards.Each platform has its own idioms and norms;these nominal or de facto standards inform user expectationsof how an application should behave. Thanks, in part to the web,users are accustomed to more customized experiences,but reflecting these platform standards can still providesignificant benefits:Reduce cognitive load—By matching the user’sexisting mental model, accomplishing tasks becomes intuitive,which requires less thinking,boosts productivity, and reduces frustrations.Build trust—Users can become wary or suspiciouswhen applications don’t adhere to their expectations.Conversely, a UI that feels familiar can build user trustand can help improve the perception of quality.This often has the added benefit of better app storeratings—something we can all appreciate!<topic_end><topic_start>Consider expected behavior on each platformThe first step is to spend some time considering whatthe expected appearance, presentation, or behavior is on this platform.Try to forget any limitations of your current implementation,and just envision the ideal user experience.Work backwards from there.Another way to think about this is to ask,“How would a user of this platform expect to achieve this goal?”Then, try to envision how that would work in your appwithout any compromises.This can be difficult if you aren’t a regular user of the platform.You might be unaware of the specific idioms and can easily missthem completely. For example, a lifetime Android user islikely unaware of platform conventions on iOS,and the same holds true for macOS, Linux, and Windows.These differences might be subtle to you,but be painfully obvious to an experienced user.<topic_end><topic_start>Find a platform advocateIf possible, assign someone as an advocate for each platform.Ideally, your advocate uses the platform as their primary device,and can offer the perspective of a highly opinionated user.To reduce the number of people, combine roles.Have one advocate for Windows and Android,one for Linux and the web, and one for Mac and iOS.The goal is to have constant, informed feedback so the appfeels great on each platform. Advocates should be encouragedto be quite picky, calling out anything they feel differs fromtypical applications on their device. A simple example is howthe default button in a dialog is typically on the left on Macand Linux, but is on the right on Windows.Details like that are easy to miss if you aren’t using a platformon a regular basis.Important: Advocates don’t need to be developers or  even full-time team members. They can be designers,  stakeholders, or external testers that are provided  with regular builds.<topic_end><topic_start>Stay uniqueConforming to expected behaviors doesn’t mean that your appneeds to use default components or styling.Many of the most popular multiplatform apps have very distinctand opinionated UIs including custom buttons, context menus,and title bars.The more you can consolidate styling and behavior across platforms,the easier development and testing will be.The trick is to balance creating a unique experience with astrong identity, while respecting the norms of each platform.<topic_end><topic_start>Common idioms and norms to considerTake a quick look at a few specific norms and idiomsyou might want to consider, and how you could approachthem in Flutter.<topic_end><topic_start>Scrollbar appearance and behaviorDesktop and mobile users expect scrollbars,but they expect them to behave differently on different platforms.Mobile users expect smaller scrollbars that only appearwhile scrolling, whereas desktop users generally expectomnipresent, larger scrollbars that they can click or drag.Flutter comes with a built-in Scrollbar widget that alreadyhas support for adaptive colors and sizes according to thecurrent platform. The one tweak you might want to make is totoggle alwaysShown when on a desktop platform:<code_start>return Scrollbar(  thumbVisibility: DeviceType.isDesktop,  controller: _scrollController,  child: GridView.count(    controller: _scrollController,    padding: const EdgeInsets.all(Insets.extraLarge),    childAspectRatio: 1,    crossAxisCount: colCount,    children: listChildren,  ),);<code_end>This subtle attention to detail can make your app feel morecomfortable on a given platform.<topic_end><topic_start>Multi-selectDealing with multi-select within a list is another areawith subtle differences across platforms:<code_start>static bool get isSpanSelectModifierDown =>    isKeyDown({LogicalKeyboardKey.shiftLeft, LogicalKeyboardKey.shiftRight});<code_end>To perform a platform-aware check for control or command,you can write something like this:<code_start>static bool get isMultiSelectModifierDown {  bool isDown = false;  if (Platform.isMacOS) {    isDown = isKeyDown(      {LogicalKeyboardKey.metaLeft, LogicalKeyboardKey.metaRight},    );  } else {    isDown = isKeyDown(      {LogicalKeyboardKey.controlLeft, LogicalKeyboardKey.controlRight},    );  }  return isDown;}<code_end>A final consideration for keyboard users is the Select All action.If you have a large list of items of selectable items,many of your keyboard users will expect that they can useControl+A to select all the items.<topic_end><topic_start>Touch devicesOn touch devices, multi-selection is typically simplified,with the expected behavior being similar to having theisMultiSelectModifier down on the desktop.You can select or deselect items using a single tap,and will usually have a button to Select All orClear the current selection.How you handle multi-selection on different devices dependson your specific use cases, but the important thing is tomake sure that you’re offering each platform the bestinteraction model possible.<topic_end><topic_start>Selectable textA common expectation on the web (and to a lesser extent desktop)is that most visible text can be selected with the mouse cursor.When text is not selectable,users on the web tend to have an adverse reaction.Luckily, this is easy to support with the SelectableText widget:<code_start>return const SelectableText('Select me!');<code_end>To support rich text, then use TextSpan:<code_start>return const SelectableText.rich(  TextSpan(    children: [      TextSpan(text: 'Hello'),      TextSpan(text: 'Bold', style: TextStyle(fontWeight: FontWeight.bold)),    ],  ),);<code_end><topic_end><topic_start>Title barsOn modern desktop applications, it’s common to customizethe title bar of your app window, adding a logo forstronger branding or contextual controls to help savevertical space in your main UI.This isn’t supported directly in Flutter, but you can use thebits_dojo package to disable the native title bars,and replace them with your own.This package lets you add whatever widgets you want to theTitleBar because it uses pure Flutter widgets under the hood.This makes it easy to adapt the title bar as you navigateto different sections of the app.<topic_end><topic_start>Context menus and tooltipsOn desktop, there are several interactions thatmanifest as a widget shown in an overlay,but with differences in how they’re triggered, dismissed,and positioned:Context menu—Typically triggered by a right-click,a context menu is positioned close to the mouse,and is dismissed by clicking anywhere,selecting an option from the menu, or clicking outside it.Tooltip—Typically triggered by hovering for200-400ms over an interactive element,a tooltip is usually anchored to a widget(as opposed to the mouse position) and is dismissedwhen the mouse cursor leaves that widget.Popup panel (also known as flyout)—Similar to a tooltip,a popup panel is usually anchored to a widget.The main difference is that panels are most oftenshown on a tap event, and they usually don’t hidethemselves when the cursor leaves.Instead, panels are typically dismissed by clickingoutside the panel or by pressing a Close or Submit button.To show basic tooltips in Flutter,use the built-in Tooltip widget:<code_start>return const Tooltip(  message: 'I am a Tooltip',  child: Text('Hover over the text to show a tooltip.'),);<code_end>Flutter also provides built-in context menus when editingor selecting text.To show more advanced tooltips, popup panels,or create custom context menus,you either use one of the available packages,or build it yourself using a Stack or Overlay.Some available packages include:While these controls can be valuable for touch users as accelerators,they are essential for mouse users. These users expectto right-click things, edit content in place,and hover for more information. Failing to meet those expectationscan lead to disappointed users, or at least,a feeling that something isn’t quite right.<topic_end><topic_start>Horizontal button orderOn Windows, when presenting a row of buttons,the confirmation button is placed at the start ofthe row (left side). On all other platforms,it’s the opposite. The confirmation button isplaced at the end of the row (right side).This can be easily handled in Flutter using theTextDirection property on Row:<code_start>TextDirection btnDirection =    DeviceType.isWindows ? TextDirection.rtl : TextDirection.ltr;return Row(  children: [    const Spacer(),    Row(      textDirection: btnDirection,      children: [        DialogButton(          label: 'Cancel',          onPressed: () => Navigator.pop(context, false),        ),        DialogButton(          label: 'Ok',          onPressed: () => Navigator.pop(context, true),        ),      ],    ),  ],);<code_end><topic_end><topic_start>Menu barAnother common pattern on desktop apps is the menu bar.On Windows and Linux, this menu lives as part of the Chrome title bar,whereas on macOS, it’s located along the top of the primary screen.Currently, you can specify custom menu bar entries usinga prototype plugin, but it’s expected that this functionality willeventually be integrated into the main SDK.It’s worth mentioning that on Windows and Linux,you can’t combine a custom title bar with a menu bar.When you create a custom title bar,you’re replacing the native one completely,which means you also lose the integrated native menu bar.If you need both a custom title bar and a menu bar,you can achieve that by implementing it in Flutter,similar to a custom context menu.<topic_end><topic_start>Drag and dropOne of the core interactions for both touch-based andpointer-based inputs is drag and drop. Although thisinteraction is expected for both types of input,there are important differences to think about whenit comes to scrolling lists of draggable items.Generally speaking, touch users expect to see drag handlesto differentiate draggable areas from scrollable ones,or alternatively, to initiate a drag by using a longpress gesture. This is because scrolling and draggingare both sharing a single finger for input.Mouse users have more input options. They can use a wheelor scrollbar to scroll, which generally eliminates the needfor dedicated drag handles. If you look at the macOSFinder or Windows Explorer, you’ll see that they workthis way: you just select an item and start dragging.In Flutter, you can implement drag and drop in manyways. Discussing specific implementations is outsidethe scope of this article, but some high level optionsare:Use the Draggable and DragTarget APIsdirectly for a custom look and feel.Hook into onPan gesture events,and move an object yourself within a parent Stack.Use one of the pre-made list packages on pub.dev.<topic_end><topic_start>Educate yourself on basic usability principlesOf course, this page doesn’t constitute an exhaustive listof the things you might consider. The more operating systems,form factors, and input devices you support,the more difficult it becomes to spec out every permutation in design.Taking time to learn basic usability principles as adeveloper empowers you to make better decisions,reduces back-and-forth iterations with design during production,and results in improved productivity with better outcomes.Here are some resources to get you started:<topic_end><topic_start>Update the UI based on orientationIn some situations,you want to update the display of an app when the userrotates the screen from portrait mode to landscape mode. For example,the app might show one item after the next in portrait mode,yet put those same items side-by-side in landscape mode.In Flutter, you can build different layouts dependingon a given Orientation.In this example, build a list that displays two columns inportrait mode and three columns in landscape mode using thefollowing steps:<topic_end><topic_start>1. Build a GridView with two columnsFirst, create a list of items to work with.Rather than using a normal list,create a list that displays items in a grid.For now, create a grid with two columns.<code_start>return GridView.count(  // A list with 2 columns  crossAxisCount: 2,  // ...);<code_end>To learn more about working with GridViews,see the Creating a grid list recipe.<topic_end><topic_start>2. Use an OrientationBuilder to change the number of columnsTo determine the app’s current Orientation, use theOrientationBuilder widget.The OrientationBuilder calculates the current Orientation bycomparing the width and height available to the parent widget,and rebuilds when the size of the parent changes.Using the Orientation, build a list that displays two columns in portraitmode, or three columns in landscape mode.<code_start>body: OrientationBuilder(  builder: (context, orientation) {    return GridView.count(      // Create a grid with 2 columns in portrait mode,      // or 3 columns in landscape mode.      crossAxisCount: orientation == Orientation.portrait ? 2 : 3,    );  },),<code_end>info Note  If you’re interested in the orientation of the screen,  rather than the amount of space available to the parent,  use MediaQuery.of(context).orientation instead of an  OrientationBuilder widget.<topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() {  runApp(const MyApp());}class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const appTitle = 'Orientation Demo';    return const MaterialApp(      title: appTitle,      home: OrientationList(        title: appTitle,      ),    );  }}class OrientationList extends StatelessWidget {  final String title;  const OrientationList({super.key, required this.title});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: Text(title)),      body: OrientationBuilder(        builder: (context, orientation) {          return GridView.count(            // Create a grid with 2 columns in portrait mode, or 3 columns in            // landscape mode.            crossAxisCount: orientation == Orientation.portrait ? 2 : 3,            // Generate 100 widgets that display their index in the List.            children: List.generate(100, (index) {              return Center(                child: Text(                  'Item $index',                  style: Theme.of(context).textTheme.displayLarge,                ),              );            }),          );        },      ),    );  }}<code_end><topic_end><topic_start>Locking device orientationIn the previous section, you learned how to adapt the app UI to device orientation changes.Flutter also allows you to specify the orientations your app supports using the values of DeviceOrientation. You can either:In the application main() method,call SystemChrome.setPreferredOrientations()with the list of preferred orientations that your app supports.To lock the device to a single orientation, you can pass a list with a single item.For a list of all the possible values, check out DeviceOrientation.<code_start>void main() {  WidgetsFlutterBinding.ensureInitialized();  SystemChrome.setPreferredOrientations([    DeviceOrientation.portraitUp,  ]);  runApp(const MyApp());}<code_end><topic_end><topic_start>Use themes to share colors and font stylesinfo Note  This recipe uses Flutter’s support for Material 3 and  the google_fonts package. As of the Flutter 3.16 release,  Material 3 is Flutter’s default theme.To share colors and font styles throughout an app, use themes.You can define app-wide themes.You can extend a theme to change a theme style for one component.Each theme defines the colors, type style, and other parametersapplicable for the type of Material component.Flutter applies styling in the following order:After you define a Theme, use it within your own widgets.Flutter’s Material widgets use your theme to set the backgroundcolors and font styles for app bars, buttons, checkboxes, and more.<topic_end><topic_start>Create an app themeTo share a Theme across your entire app, set the theme propertyto your MaterialApp constructor.This property takes a ThemeData instance.As of the Flutter 3.16 release, Material 3 is Flutter’sdefault theme.If you don’t specify a theme in the constructor,Flutter creates a default theme for you.<code_start>MaterialApp(  title: appName,  theme: ThemeData(    useMaterial3: true,    // Define the default brightness and colors.    colorScheme: ColorScheme.fromSeed(      seedColor: Colors.purple,      // ···      brightness: Brightness.dark,    ),    // Define the default `TextTheme`. Use this to specify the default    // text styling for headlines, titles, bodies of text, and more.    textTheme: TextTheme(      displayLarge: const TextStyle(        fontSize: 72,        fontWeight: FontWeight.bold,      ),      // ···      titleLarge: GoogleFonts.oswald(        fontSize: 30,        fontStyle: FontStyle.italic,      ),      bodyMedium: GoogleFonts.merriweather(),      displaySmall: GoogleFonts.pacifico(),    ),  ),  home: const MyHomePage(    title: appName,  ),);<code_end>Most instances of ThemeData set values for the following two properties. These properties affect the entire app.To learn what colors, fonts, and other properties, you can define,check out the ThemeData documentation.<topic_end><topic_start>Apply a themeTo apply your new theme, use the Theme.of(context) methodwhen specifying a widget’s styling properties.These can include, but are not limited to, style and color.The Theme.of(context) method looks up the widget tree and retrievesthe nearest Theme in the tree.If you have a standalone Theme, that’s applied.If not, Flutter applies the app’s theme.In the following example, the Container constructor uses this technique to set its color.<code_start>Container(  padding: const EdgeInsets.symmetric(    horizontal: 12,    vertical: 12,  ),  color: Theme.of(context).colorScheme.primary,  child: Text(    'Text with a background color',    // ···    style: Theme.of(context).textTheme.bodyMedium!.copyWith(          color: Theme.of(context).colorScheme.onPrimary,        ),  ),),<code_end><topic_end><topic_start>Override a themeTo override the overall theme in part of an app,wrap that section of the app in a Theme widget.You can override a theme in two ways:<topic_end><topic_start>Set a unique ThemeData instanceIf you want a component of your app to ignore the overall theme,create a ThemeData instance.Pass that instance to the Theme widget.<code_start>Theme(  // Create a unique theme with `ThemeData`.  data: ThemeData(    colorScheme: ColorScheme.fromSeed(      seedColor: Colors.pink,    ),  ),  child: FloatingActionButton(    onPressed: () {},    child: const Icon(Icons.add),  ),);<code_end><topic_end><topic_start>Extend the parent themeInstead of overriding everything, consider extending the parent theme.To extend a theme, use the copyWith() method.<code_start>Theme(  // Find and extend the parent theme using `copyWith`.  // To learn more, check out the section on `Theme.of`.  data: Theme.of(context).copyWith(    colorScheme: ColorScheme.fromSeed(      seedColor: Colors.pink,    ),  ),  child: const FloatingActionButton(    onPressed: null,    child: Icon(Icons.add),  ),);<code_end><topic_end><topic_start>Watch a video on ThemeTo learn more, watch this short Widget of the Week video on the Theme widget:<topic_end><topic_start>Try an interactive example<code_start>import 'package:flutter/material.dart';// Include the Google Fonts package to provide more text format options// https://pub.dev/packages/google_fontsimport 'package:google_fonts/google_fonts.dart';void main() {  runApp(const MyApp());}class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const appName = 'Custom Themes';    return MaterialApp(      title: appName,      theme: ThemeData(        useMaterial3: true,        // Define the default brightness and colors.        colorScheme: ColorScheme.fromSeed(          seedColor: Colors.purple,          // TRY THIS: Change to "Brightness.light"          //           and see that all colors change          //           to better contrast a light background.          brightness: Brightness.dark,        ),        // Define the default `TextTheme`. Use this to specify the default        // text styling for headlines, titles, bodies of text, and more.        textTheme: TextTheme(          displayLarge: const TextStyle(            fontSize: 72,            fontWeight: FontWeight.bold,          ),          // TRY THIS: Change one of the GoogleFonts          //           to "lato", "poppins", or "lora".          //           The title uses "titleLarge"          //           and the middle text uses "bodyMedium".          titleLarge: GoogleFonts.oswald(            fontSize: 30,            fontStyle: FontStyle.italic,          ),          bodyMedium: GoogleFonts.merriweather(),          displaySmall: GoogleFonts.pacifico(),        ),      ),      home: const MyHomePage(        title: appName,      ),    );  }}class MyHomePage extends StatelessWidget {  final String title;  const MyHomePage({super.key, required this.title});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text(title,            style: Theme.of(context).textTheme.titleLarge!.copyWith(                  color: Theme.of(context).colorScheme.onSecondary,                )),        backgroundColor: Theme.of(context).colorScheme.secondary,      ),      body: Center(        child: Container(          padding: const EdgeInsets.symmetric(            horizontal: 12,            vertical: 12,          ),          color: Theme.of(context).colorScheme.primary,          child: Text(            'Text with a background color',            // TRY THIS: Change the Text value            //           or change the Theme.of(context).textTheme            //           to "displayLarge" or "displaySmall".            style: Theme.of(context).textTheme.bodyMedium!.copyWith(                  color: Theme.of(context).colorScheme.onPrimary,                ),          ),        ),      ),      floatingActionButton: Theme(        data: Theme.of(context).copyWith(          // TRY THIS: Change the seedColor to "Colors.red" or          //           "Colors.blue".          colorScheme: ColorScheme.fromSeed(            seedColor: Colors.pink,            brightness: Brightness.dark,          ),        ),        child: FloatingActionButton(          onPressed: () {},          child: const Icon(Icons.add),        ),      ),    );  }}<code_end><topic_end><topic_start>Material Design for FlutterMaterial Design is an open-source design system builtand supported by Google designers and developers.The latest version, Material 3, enables personal,adaptive, and expressive experiences—from dynamic colorand enhanced accessibility, to foundations forlarge screen layouts, and design tokens.warning Warning  As of the Flutter 3.16 release, Material 3 is  enabled by default. For now, you can opt out  of Material 3 by setting the useMaterial3 property  to false. But be aware that the useMaterial3  property and support for Material 2  will eventually be deprecated according to  Flutter’s deprecation policy.For most Flutter widgets, upgrading to Material 3is seamless. But some widgets couldn’t beupdated—entirely new implementations were needed,such as NavigationBar.You must make these changes to your code manually.Until your app is entirely updated,the UI might look or act a bit strange.You can find the entirely new Material components byvisiting the Affected widgets page.Explore the updated components, typography, color system,and elevation support with theinteractive Material 3 demo:<topic_end><topic_start>More informationTo learn more about Material Design and Flutter,check out:<topic_end><topic_start>Migrate to Material 3<topic_end><topic_start>SummaryThe Material library has been updated to match the Material 3 Design spec.Changes include new components and component themes, updated component visuals,and much more. Many of these updates are seamless. You’ll see the new versionof an affected widget when recompiling your app against the 3.16 (or later)release. But some manual work is also required to complete the migration.<topic_end><topic_start>Migration guidePrior to the 3.16 release, you could opt in to the Material 3 changes bysetting the useMaterial3 flag to true. As of the Flutter 3.16 release(November 2023), useMaterial3 is true by default.By the way, you can revert to Material 2 behavior in your app by setting theuseMaterial3 to false. However, this is just a temporary solution. TheuseMaterial3 flag and the Material 2 implementation will eventually beremoved as part of Flutter’s deprecation policy.<topic_end><topic_start>ColorsThe default values for ThemeData.colorScheme are updated to matchthe Material 3 Design spec.The ColorScheme.fromSeed constructor generates a ColorSchemederived from the given seedColor. The colors generated by thisconstructor are designed to work well together and meet contrastrequirements for accessibility in the Material 3 Design system.When updating to the 3.16 release, your UI might look a little strangewithout the correct ColorScheme. To fix this, migrate to theColorScheme generated from the ColorScheme.fromSeed constructor.Code before migration:Code after migration:To generate a content-based dynamic color scheme, use theColorScheme.fromImageProvider static method. For an example of generating acolor scheme, check out the ColorScheme from a network image sample.Changes to Flutter Material 3 include a new background color.ColorScheme.surfaceTint indicates an elevated widget.Some widgets use different colors.To return your app’s UI to its previous behavior (which we don’t recommend):Code before migration:Code after migration:The ColorScheme.surfaceTint value indicates a component’s elevation inMaterial 3. Some widgets might use both surfaceTint and shadowColor toindicate elevation (for example, Card and ElevatedButton) and others mightonly use surfaceTint to indicate elevation (such as AppBar).To return to the widget’s previous behavior, set, set Colors.transparentto ColorScheme.surfaceTint in the theme. To differentiate a widget’s shadowfrom the content (when it has no shadow), set the ColorScheme.shadow color tothe shadowColor property in the widget theme without a default shadow color.Code before migration:Code after migration:The ElevatedButton now styles itself with a new combination of colors.Previously, when the useMaterial3 flag was set to false, ElevatedButtonstyled itself with ColorScheme.primary for the background andColorScheme.onPrimary for the foreground. To achieve the same visuals, switchto the new FilledButton widget without the elevation changes or drop shadow.Code before migration:Code after migration:<topic_end><topic_start>TypographyThe default values for ThemeData.textTheme are updated to match theMaterial 3 defaults. Changes include updated font size, font weight, letterspacing, and line height. For more details, check out the TextThemedocumentation.As shown in the following example, prior to the 3.16 release, when a Textwidget with a long string using TextTheme.bodyLarge in a constrained layoutwrapped the text into two lines. However, the 3.16 release wraps the text intothree lines. If you must achieve the previous behavior, adjust the text styleand, if necessary, the letter spacing.Code before migration:Code after migration:<topic_end><topic_start>ComponentsSome components couldn’t merely be updated to match the Material 3 Design specbut needed a whole new implementation. Such components require manual migrationsince the Flutter SDK doesn’t know what, exactly, you want.Replace the Material 2 style BottomNavigationBar widget with the newNavigationBar widget. It’s slightly taller, contains pill-shapednavigation indicators, and uses new color mappings.Code before migration:Code after migration:Check out the complete sample onmigrating from BottomNavigationBar to NavigationBar.Replace the Drawer widget with NavigationDrawer, which providespill-shaped navigation indicators, rounded corners, and new color mappings.Code before migration:Code after migration:Check out the complete sample on migrating from Drawer to NavigationDrawer.Material 3 introduces medium and large app bars that display a larger headlinebefore scrolling. Instead of a drop shadow, ColorScheme.surfaceTint coloris used create a separation from the content when scrolling.The following code demonstrates how to implement the medium app bar:There are now two types of TabBar widgets: primary and secondary.Secondary tabs are used within a content area to further separaterelated content and establish hierarchy. Check out the TabBar.secondaryexample.The new TabBar.tabAlignment property specifies the horizontal alignmentof the tabs.The following sample shows how to modify tab alignment in a scrollable TabBar:SegmentedButton, an updated version of ToggleButtons,uses fully rounded corners, differs in layout height andsize, and uses a Dart Set to determine selected items.Code before migration:Code after migration:Check out the complete sample onmigrating from ToggleButtons to SegmentedButton.<topic_end><topic_start>New components<topic_end><topic_start>TimelineIn stable release: 3.16<topic_end><topic_start>ReferencesDocumentation:API documentation:Relevant issues:Relevant PRs:<topic_end><topic_start>Text<topic_end><topic_start>Topics<topic_end><topic_start>Flutter's fonts and typographyTypography covers the style and appearance oftype or fonts: it specifies how heavy the font is,the slant of the font, the spacing betweenthe letters, and other visual aspects of the text.All fonts are not created the same. Fonts are a hugetopic and beyond the scope of this site, however,this page discusses Flutter’s support for variableand static fonts.<topic_end><topic_start>Variable fontsVariable fonts (also called OpenType fonts),allow you to control pre-defined aspects of text styling.Variable fonts support specific axes, such as width,weight, slant (to name a few).The user can select any value along the continuous axiswhen specifying the type.However, the font must first define what axes are available,and that isn’t always easy to figure out. If you are usinga Google Font, you can learn what axes are available usingthe type tester feature, described in the next section.<topic_end><topic_start>Using the Google Fonts type testerThe Google Fonts site offers both variable and static fonts.Use the type tester to learn more about its variable fonts.In real time, move the slider on any of the axes tosee how it affects the font. When programming a variable font,use the FontVariation class to modify the font’s design axes.The FontVariation class conforms to theOpenType font variables spec.<topic_end><topic_start>Static fontsGoogle Fonts also contains static fonts. As with variable fonts,you need to know how the font is designed to know what optionsare available to you.Once again, the Google Fonts site can help.<topic_end><topic_start>Using the Google Fonts siteUse the font’s details page to learn more about its static fonts.Use the following API to programmatically alter a static font(but remember that this only works if the font was designedto support the feature):A FontFeature corresponds to an OpenType feature tagand can be thought of as a boolean flag to enable or disablea feature of a given font.The following example is for CSS, but illustrates the concept:<topic_end><topic_start>Other resourcesThe following video shows you some of the capabilitiesof Flutter’s typography and combines it with the Materialand Cupertino look and feel (depending on the platformthe app runs on), animation, and custom fragment shaders:Prototyping beautiful designs with FlutterTo read one engineer’s experiencecustomizing variable fonts and animating them as theymorph (and was the basis for the above video),check out Playful typography with Flutter,a free article on Medium. The associated example alsouses a custom shader.<topic_end><topic_start>Use a custom font<topic_end><topic_start>What you’ll learnAlthough Android and iOS offer high quality system fonts,designers want support for custom fonts.You might have a custom-built font from a designer,or perhaps you downloaded a font from Google Fonts.A typeface is the collection of glyphs or shapes that comprisea given style of lettering.A font is one representation of that typeface at a given weight or variation.Roboto is a typeface and Roboto Bold is a font.Flutter lets you apply a custom font across an entire app or to individual widgets.This recipe creates an app that uses custom fonts with the following steps.You don’t need to follow each step as you go. The guide offers completed example files at the end.info NoteThis guide makes the following presumptions:<topic_end><topic_start>Choose a fontYour choice of font should be more than a preference.Consider which file formats work with Flutter andhow the font could affect design options and app performance.<topic_end><topic_start>Pick a supported font formatFlutter supports the following font formats:Flutter does not support fonts in the Web Open Font Format,.woff and .woff2, on desktop platforms.<topic_end><topic_start>Choose fonts for their specific benefitsFew sources agree on what a font file type is or which uses less space.The key difference between font file types involves how the format encodes the glyphs in the file.Most TrueType and OpenType font files have similar capabilities as theyborrowed from each other as the formats and fonts improved over time.Which font you should use depends on the following considerations.Research what options a given font offers,like more than one weight or style per font file,variable font capability,the availability of multiple font files for a multiple font weights,or more than one width per font.Choose the typeface or font family that meets the design needs of your app.To learn how to get direct access to over 1,000 open-sourced font families,  check out the google_fonts package.To learn about another approach to using custom fonts that allows you to  re-use one font over multiple projects,  check out Export fonts from a package.<topic_end><topic_start>Import the font filesTo work with a font, import its font files into your Flutter project.To import font files, perform the following steps.If necessary, to match the remaining steps in this guide,change the name of your Flutter app to custom_fonts.Navigate to the root of your Flutter project.Create a fonts directory at the root of your Flutter project.Move or copy the font files in a fonts or assetsfolder at the root of your Flutter project.The resulting folder structure should resemble the following:<topic_end><topic_start>Declare the font in the pubspec.yaml fileAfter you’ve downloaded a font,include a font definition in the pubspec.yaml file.This font definition also specifies which font file should be used torender a given weight or style in your app.<topic_end><topic_start>Define fonts in the pubspec.yaml fileTo add font files to your Flutter app, complete the following steps.Open the pubspec.yaml file at the root of your Flutter project.Paste the following YAML block after the flutter declaration.This pubspec.yaml file defines the italic style for theRaleway font family as the Raleway-Italic.ttf font file.When you set style: TextStyle(fontStyle: FontStyle.italic),Flutter swaps Raleway-Regular with Raleway-Italic.The family value sets the name of the typeface.You use this name in the fontFamily property of a TextStyle object.The value of an asset is a relative path from the pubspec.yaml fileto the font file.These files contain the outlines for the glyphs in the font.When building the app,Flutter includes these files in the app’s asset bundle.<topic_end><topic_start>Include font files for each fontDifferent typefaces implement font files in different ways.If you need a typeface with a variety of font weights and styles,choose and import font files that represent that variety.When you import a font file that doesn’t include either multiple fontswithin it or variable font capabilities,don’t use the style or weight property to adjust how they display.If you do use those properties on a regular font file,Flutter attempts to simulate the look.The visual result will look quite different from using the correct font file.<topic_end><topic_start>Set styles and weights with font filesWhen you declare which font files represent styles or weights of a font,you can apply the style or weight properties.<topic_end><topic_start>Set font weightThe weight property specifies the weight of the outlines inthe file as an integer multiple of 100, between 100 and 900.These values correspond to the FontWeight and can be used in thefontWeight property of a TextStyle object.In the pubspec.yaml shown in this guide,you defined RobotoMono-Bold as the 700 weight of the font family.To use the RobotoMono-Bold font that you added to your app, set fontWeight to FontWeight.w700 in your TextStyle widget.If hadn’t added RobotoMono-Bold to your app,Flutter attempts to make the font look bold.The text then might appear to be somewhat darker.You can’t use the weight property to override the weight of the font.You can’t set RobotoMono-Bold to any other weight than 700.If you set TextStyle(fontFamily: 'RobotoMono', fontWeight: FontWeight.w900),the displayed font would still render as however bold RobotoMono-Bold looks.<topic_end><topic_start>Set font styleThe style property specifies whether the glyphs in the font file display aseither italic or normal.These values correspond to the FontStyle.You can use these styles in the fontStyle propertyof a TextStyle object.In the pubspec.yaml shown in this guide,you defined Raleway-Italic as being in the italic style. To use the Raleway-Italic font that you added to your app, set style: TextStyle(fontStyle: FontStyle.italic).Flutter swaps Raleway-Regular with Raleway-Italic when rendering.If hadn’t added Raleway-Italic to your app,Flutter attempts to make the font look italic.The text then might appear to be leaning to the right.You can’t use the style property to override the glyphs of a font.If you set TextStyle(fontFamily: 'Raleway', fontStyle: FontStyle.normal),the displayed font would still render as italic.The regular style of an italic font is italic.<topic_end><topic_start>Set a font as the defaultTo apply a font to text, you can set the font as the app’s default fontin its theme.To set a default font, set the fontFamily property in the app’s theme.Match the fontFamily value to the family name declared in thepubspec.yaml file.The result would resemble the following code.<code_start>return MaterialApp(  title: 'Custom Fonts',  // Set Raleway as the default app font.  theme: ThemeData(fontFamily: 'Raleway'),  home: const MyHomePage(),);<code_end>To learn more about themes,check out the Using Themes to share colors and font styles recipe.<topic_end><topic_start>Set the font in a specific widgetTo apply the font to a specific widget like a Text widget,provide a TextStyle to the widget.For this guide, try to apply the RobotoMono font to a single Text widget.Match the fontFamily value to the family name declared in thepubspec.yaml file.The result would resemble the following code.<code_start>child: Text(  'Roboto Mono sample',  style: TextStyle(fontFamily: 'RobotoMono'),),<code_end>error Important  If a TextStyle object specifies a weight or style without a  corresponding font file, the engine uses a generic file for the font  and attempts to extrapolate outlines for the requested weight and style.Avoid relying on this capability. Import the proper font file instead.<topic_end><topic_start>Try the complete example<topic_end><topic_start>Download fontsDownload the Raleway and RobotoMono font files from Google Fonts.<topic_end><topic_start>Update the pubspec.yaml fileOpen the pubspec.yaml file at the root of your Flutter project.Replace its contents with the following YAML.<topic_end><topic_start>Use this main.dart fileOpen the main.dart file in the lib/ directory of your Flutter project.Replace its contents with the following Dart code.<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Custom Fonts',      // Set Raleway as the default app font.      theme: ThemeData(fontFamily: 'Raleway'),      home: const MyHomePage(),    );  }}class MyHomePage extends StatelessWidget {  const MyHomePage({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      // The AppBar uses the app-default Raleway font.      appBar: AppBar(title: const Text('Custom Fonts')),      body: const Center(        // This Text widget uses the RobotoMono font.        child: Text(          'Roboto Mono sample',          style: TextStyle(fontFamily: 'RobotoMono'),        ),      ),    );  }}<code_end>The resulting Flutter app should display the following screen.<topic_end><topic_start>Export fonts from a packageRather than declaring a font as part of an app,you can declare a font as part of a separate package.This is a convenient way to share the same font acrossseveral different projects,or for coders publishing their packages to pub.dev.This recipe uses the following steps:info Note  Check out the google_fonts package for direct access  to almost 1000 open-sourced font families.<topic_end><topic_start>1. Add a font to a packageTo export a font from a package, you need to import the font files into thelib folder of the package project. You can place font files directly in thelib folder or in a subdirectory, such as lib/fonts.In this example, assume you’ve got a Flutter library calledawesome_package with fonts living in a lib/fonts folder.<topic_end><topic_start>2. Add the package and fonts to the appNow you can use the fonts in the package byupdating the pubspec.yaml in the app’s root directory.<topic_end><topic_start>Add the package to the appTo add the awesome_package package as a dependency,run flutter pub add:<topic_end><topic_start>Declare the font assetsNow that you’ve imported the package, tell Flutter where tofind the fonts from the awesome_package.To declare package fonts, prefix the path to the font withpackages/awesome_package.This tells Flutter to look in the lib folderof the package for the font.<topic_end><topic_start>3. Use the fontUse a TextStyle to change the appearance of text.To use package fonts, declare which font you’d like to use andwhich package the font belongs to.<code_start>child: Text(  'Using the Raleway font from the awesome_package',  style: TextStyle(    fontFamily: 'Raleway',  ),),<code_end><topic_end><topic_start>Complete example<topic_end><topic_start>FontsThe Raleway and RobotoMono fonts were downloaded fromGoogle Fonts.<topic_end><topic_start>pubspec.yaml<topic_end><topic_start>main.dart<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Package Fonts',      home: MyHomePage(),    );  }}class MyHomePage extends StatelessWidget {  const MyHomePage({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      // The AppBar uses the app-default font.      appBar: AppBar(title: const Text('Package Fonts')),      body: const Center(        // This Text widget uses the Raleway font.        child: Text(          'Using the Raleway font from the awesome_package',          style: TextStyle(            fontFamily: 'Raleway',          ),        ),      ),    );  }}<code_end><topic_end><topic_start>Custom drawing and graphics<topic_end><topic_start>Topics<topic_end><topic_start>Writing and using fragment shadersinfo Note  Both the Skia and Impeller backends support writing a  custom shader. Except where noted, the same  instructions apply to both.Custom shaders can be used to provide rich graphical effectsbeyond those provided by the Flutter SDK.A shader is a program authored in a small,Dart-like language, known as GLSL,and executed on the user’s GPU.Custom shaders are added to a Flutter projectby listing them in the pubspec.yaml file,and obtained using the FragmentProgram API.<topic_end><topic_start>Adding shaders to an applicationShaders, in the form of GLSL files with the .frag extension,must be declared in the shaders section of your project’s pubspec.yaml file.The Flutter command-line tool compiles the shaderto its appropriate backend format,and generates its necessary runtime metadata.The compiled shader is then included in the application just like an asset.When running in debug mode,changes to a shader program trigger recompilationand update the shader during hot reload or hot restart.Shaders from packages are added to a projectwith packages/$pkgname prefixed to the shader program’s name(where $pkgname is the name of the package).<topic_end><topic_start>Loading shaders at runtimeTo load a shader into a FragmentProgram object at runtime,use the FragmentProgram.fromAsset constructor.The asset’s name is the same as the path to the shadergiven in the pubspec.yaml file.The FragmentProgram object can be used to createone or more FragmentShader instances.A FragmentShader object represents a fragment programalong with a particular set of uniforms (configuration parameters).The available uniforms depends on how the shader was defined.<topic_end><topic_start>Canvas APIFragment shaders can be used with most Canvas APIsby setting Paint.shader.For example, when using Canvas.drawRectthe shader is evaluated for all fragments within the rectangle.For an API like Canvas.drawPath with a stroked path,the shader is evaluated for all fragments within the stroked line.Some APIs, such as Canvas.drawImage, ignore the value of the shader.<topic_end><topic_start>Authoring shadersFragment shaders are authored as GLSL source files.By convention, these files have the .frag extension.(Flutter doesn’t support vertex shaders,which would have the .vert extension.)Any GLSL version from 460 down to 100 is supported,though some available features are restricted.The rest of the examples in this document use version 460 core.Shaders are subject to the following limitationswhen used with Flutter:<topic_end><topic_start>UniformsA fragment program can be configured by defininguniform values in the GLSL shader sourceand then setting these values in Dart foreach fragment shader instance.Floating point uniforms with the GLSL typesfloat, vec2, vec3, and vec4are set using the FragmentShader.setFloat method.GLSL sampler values, which use the sampler2D type,are set using the FragmentShader.setImageSampler method.The correct index for each uniform value is determined by the orderthat the uniform values are defined in the fragment program.For data types composed of multiple floats, such as a vec4,you must call FragmentShader.setFloat once for each value.For example, given the following uniforms declarations in a GLSL fragment program:The corresponding Dart code to initialize these uniform values is as follows:Observe that the indices used with FragmentShader.setFloatdo not count the sampler2D uniform.This uniform is set separately with FragmentShader.setImageSampler,with the index starting over at 0.Any float uniforms that are left uninitialized will default to 0.0.<topic_end><topic_start>Current positionThe shader has access to a varying value that contains the local coordinates forthe particular fragment being evaluated. Use this feature to computeeffects that depend on the current position, which can be accessed byimporting the flutter/runtime_effect.glsl library and calling theFlutterFragCoord function. For example:The value returned from FlutterFragCoord is distinct from gl_FragCoord.gl_FragCoord provides the screen space coordinates and should generally beavoided to ensure that shaders are consistent across backends.When targeting a Skia backend,the calls to gl_FragCoord are rewritten to access localcoordinates but this rewriting isn’t possible with Impeller.<topic_end><topic_start>ColorsThere isn’t a built-in data type for colors.Instead they are commonly represented as a vec4with each component corresponding to one of the RGBAcolor channels.The single output fragColor expects that the color valueis normalized to be in the range of 0.0 to 1.0and that it has premultiplied alpha.This is different than typical Flutter colors which usea 0-255 value encoding and have unpremultipled alpha.<topic_end><topic_start>SamplersA sampler provides access to a dart:ui Image object.This image can be acquired either from a decoded imageor from part of the application usingScene.toImageSync or Picture.toImageSync.By default, the image usesTileMode.clamp to determine how values outsideof the range of [0, 1] behave.Customization of the tile mode is notsupported and needs to be emulated in the shader.<topic_end><topic_start>Performance considerationsWhen targeting the Skia backend,loading the shader might be expensive since itmust be compiled to the appropriateplatform-specific shader at runtime.If you intend to use one or more shaders during an animation,consider precaching the fragment program objects beforestarting the animation.You can reuse a FragmentShader object across frames;this is more efficient than creating a newFragmentShader for each frame.For a more detailed guide on writing performant shaders,check out Writing efficient shaders on GitHub.<topic_end><topic_start>Other resourcesFor more information, here are a few resources.<topic_end><topic_start>Add interactivity to your Flutter app<topic_end><topic_start>What you'll learnHow do you modify your app to make it react to user input?In this tutorial, you’ll add interactivity to an app thatcontains only non-interactive widgets.Specifically, you’ll modify an icon to make it tappableby creating a custom stateful widget that manages twostateless widgets.The building layouts tutorial showed you how to createthe layout for the following screenshot.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 41people have favorited this lake. After completing this tutorial,tapping the star removes its favorited status,replacing the solid star with an outline anddecreasing the count. Tapping again favorites the lake,drawing a solid star and increasing the count.To accomplish this, you’ll create a single custom widgetthat includes both the star and the count,which are themselves widgets. Tapping the star changes statefor 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 to Managing state.<topic_end><topic_start>Stateful and stateless widgetsA widget is either stateful or stateless. If a widget canchange—when a user interacts with it,for example—it’s stateful.A stateless widget never changes.Icon, IconButton, and Text areexamples of stateless widgets. Stateless widgetssubclass StatelessWidget.A stateful widget is dynamic: for example,it can change its appearance in response to eventstriggered by user interactions or when it receives data.Checkbox, Radio, Slider,InkWell, Form, and TextFieldare examples of stateful widgets. Stateful widgetssubclass StatefulWidget.A widget’s state is stored in a State object,separating the widget’s state from its appearance.The state consists of values that can change, like aslider’s current value or whether a checkbox is checked.When the widget’s state changes,the state object calls setState(),telling the framework to redraw the widget.<topic_end><topic_start>Creating a stateful widget<topic_end><topic_start>What's the point?In this section, you’ll create a custom stateful widget.You’ll replace two stateless widgets—the solid redstar and the numeric count next to it—with a singlecustom stateful widget that manages a row with twochildren widgets: an IconButton and Text.Implementing a custom stateful widget requires creating two classes:This section shows you how to build a stateful widget,called FavoriteWidget, for the lakes app.After setting up, your first step is choosing how state ismanaged for FavoriteWidget.<topic_end><topic_start>Step 0: Get readyIf you’ve already built the app in thebuilding layouts tutorial,skip to the next section.Once you have a connected and enabled device,or you’ve launched the iOS simulator(part of the Flutter install) or theAndroid emulator (part of the Android Studioinstall), you are good to go!<topic_end><topic_start>Step 1: Decide which object manages the widget’s stateA 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 isolatedaction that doesn’t affect the parent widget or the rest ofthe UI, so the widget can handle its state internally.Learn more about the separation of widget and state,and how state might be managed, in Managing state.<topic_end><topic_start>Step 2: Subclass StatefulWidgetThe FavoriteWidget class manages its own state,so it overrides createState() to create a Stateobject. The framework calls createState()when it wants to build the widget.In this example, createState() returns aninstance of _FavoriteWidgetState,which you’ll implement in the next step.<code_start>class FavoriteWidget extends StatefulWidget {  const FavoriteWidget({super.key});  @override  State<FavoriteWidget> createState() => _FavoriteWidgetState();}<code_end>info Note  Members or classes that start with an underscore  (_) are private. For more information,  see Libraries and imports, a section in the  Dart language documentation.<topic_end><topic_start>Step 3: Subclass StateThe _FavoriteWidgetState class stores the mutable datathat can change over the lifetime of the widget.When the app first launches, the UI displays a solidred star, indicating that the lake has “favorite” status,along with 41 likes. These values are stored in the_isFavorited and _favoriteCount fields:<code_start>class _FavoriteWidgetState extends State<FavoriteWidget> {  bool _isFavorited = true;  int _favoriteCount = 41;  // ···}<code_end>The class also defines a build() method,which creates a row containing a red IconButton,and Text.  You use IconButton (instead of Icon)because it has an onPressed property that definesthe callback function (_toggleFavorite) for handling a tap.You’ll define the callback function next.<code_start>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'),          ),        ),      ],    );  }}<code_end>lightbulb Tip  Placing the Text in a SizedBox 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, calls setState().Calling setState() is critical, because thistells the framework that the widget’s state haschanged and that the widget should be redrawn.The function argument to setState() toggles theUI between these two states:<code_start>void _toggleFavorite() {  setState(() {    if (_isFavorited) {      _favoriteCount -= 1;      _isFavorited = false;    } else {      _favoriteCount += 1;      _isFavorited = true;    }  });}<code_end><topic_end><topic_start>Step 4: Plug the stateful widget into the widget treeAdd your custom stateful widget to the widget tree inthe app’s build() method. First, locate the code thatcreates the Icon and Text, and delete it.In the same location, create the stateful widget:That’s it! When you hot reload the app,the star icon should now respond to taps.<topic_end><topic_start>Problems?If you can’t get your code to run, look in yourIDE 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 canbe managed, and lists other available interactive widgets.<topic_end><topic_start>Managing state<topic_end><topic_start>What's the point?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 waysto 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 uncheckedmode 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 thestate 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 stateby creating three simple examples: TapboxA, TapboxB,and TapboxC. The examples all work similarly—eachcreates a container that, when tapped, toggles between agreen or grey box. The _active boolean determines thecolor: green for active or grey for inactive.These examples use GestureDetector to capture activityon the Container.<topic_end><topic_start>The widget manages its own stateSometimes it makes the most sense for the widgetto manage its state internally. For example,ListView automatically scrolls when itscontent exceeds the render box. Most developersusing ListView don’t want to manage ListView’sscrolling behavior, so ListView itself manages its scroll offset.The _TapboxAState class:<code_start>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(),        ),      ),    );  }}<code_end><topic_end><topic_start>The parent widget manages the widget’s stateOften it makes the most sense for the parent widgetto manage the state and tell its child widget when to update.For example, IconButton allows you to treatan icon as a tappable button. IconButton is astateless widget because we decided that the parentwidget needs to know whether the button has been tapped,so it can take appropriate action.In the following example, TapboxB exports its stateto its parent through a callback. Because TapboxBdoesn’t manage any state, it subclasses StatelessWidget.The ParentWidgetState class:The TapboxB class:<code_start>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),          ),        ),      ),    );  }}<code_end><topic_end><topic_start>A mix-and-match approachFor some widgets, a mix-and-match approach makesthe most sense. In this scenario, the stateful widgetmanages some of the state, and the parent widgetmanages other aspects of the state.In the TapboxC example, on tap down,a dark green border appears around the box. On tap up,the border disappears and the box’s color changes. TapboxCexports its _active state to its parent but manages its_highlight state internally. This example has two Stateobjects, _ParentWidgetState and _TapboxCState.The _ParentWidgetState object:The _TapboxCState object:<code_start>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)),        ),      ),    );  }}<code_end>An alternate implementation might have exported the highlightstate 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 highlightingis managed, and prefers that the tap box handles thosedetails.<topic_end><topic_start>Other interactive widgetsFlutter offers a variety of buttons and similar interactive widgets.Most of these widgets implement the Material Design guidelines,which define a set of components with an opinionated UI.If you prefer, you can use GestureDetector to buildinteractivity into any custom widget.You can find examples of GestureDetector inManaging state. Learn more about the GestureDetectorin Handle taps, a recipe in the Flutter cookbook.lightbulb Tip  Flutter also provides a set of iOS-style widgets called  Cupertino.When you need interactivity, it’s easiest to use one ofthe prefabricated widgets. Here’s a partial list:<topic_end><topic_start>Standard widgets<topic_end><topic_start>Material Components<topic_end><topic_start>ResourcesThe following resources might help when adding interactivityto your app.Gestures, a section in the Flutter cookbook.<topic_end><topic_start>Taps, drags, and other gesturesThis document explains how to listen for, and respond to,gestures in Flutter.Examples of gestures include taps, drags, and scaling.The gesture system in Flutter has two separate layers.The first layer has raw pointer events that describethe location and movement of pointers (for example,touches, mice, and styli) across the screen.The second layer has gestures that describe semanticactions that consist of one or more pointer movements.<topic_end><topic_start>PointersPointers represent raw data about the user’s interactionwith the device’s screen.There are four types of pointer events:On pointer down, the framework does a hit test on your appto determine which widget exists at the location where thepointer contacted the screen. The pointer down event(and subsequent events for that pointer) are then dispatchedto the innermost widget found by the hit test.From there, the events bubble up the tree and are dispatchedto all the widgets on the path from the innermostwidget to the root of the tree. There is no mechanism forcanceling or stopping pointer events from being dispatched further.To listen to pointer events directly from the widgets layer, use aListener widget. However, generally,consider using gestures (as discussed below) instead.<topic_end><topic_start>GesturesGestures represent semantic actions (for example, tap, drag,and scale) that are recognized from multiple individual pointerevents, potentially even multiple individual pointers.Gestures can dispatch multiple events, corresponding to thelifecycle of the gesture (for example, drag start,drag update, and drag end):TapDouble tapLong pressVertical dragHorizontal dragPan<topic_end><topic_start>Adding gesture detection to widgetsTo listen to gestures from the widgets layer,use a GestureDetector.info Note  To learn more, watch this short  Widget of the Week video on the GestureDetector widget:If you’re using Material Components,many of those widgets already respond to taps or gestures.For example, IconButton and TextButtonrespond to presses (taps), and ListViewresponds to swipes to trigger scrolling.If you aren’t using those widgets, but you want the“ink splash” effect on a tap, you can use InkWell.<topic_end><topic_start>Gesture disambiguationAt a given location on screen,there might be multiple gesture detectors.For example:All of these gesture detectors listen to the streamof pointer events as they flow past and attempt to recognizespecific gestures. The GestureDetector widget decideswhich gestures to attempt to recognize based on which of itscallbacks are non-null.When there is more than one gesture recognizer for a givenpointer on the screen, the framework disambiguates whichgesture the user intends by having each recognizer jointhe gesture arena. The gesture arena determines whichgesture wins using the following rules:At any time, a recognizer can eliminate itself and leave thearena. If there’s only one recognizer left in the arena,that recognizer wins.At any time, a recognizer can declare itself the winner,causing all of the remaining recognizers to lose.For example, when disambiguating horizontal and vertical dragging,both recognizers enter the arena when they receive the pointerdown event. The recognizers observe the pointer move events.If the user moves the pointer more than a certain number oflogical pixels horizontally, the horizontal recognizer declaresthe win and the gesture is interpreted as a horizontal drag.Similarly, if the user moves more than a certain number of logicalpixels vertically, the vertical recognizer declares itself the winner.The gesture arena is beneficial when there is only a horizontal(or vertical) drag recognizer. In that case, there is only onerecognizer in the arena and the horizontal drag is recognizedimmediately, which means the first pixel of horizontal movementcan be treated as a drag and the user won’t need to wait forfurther gesture disambiguation.<topic_end><topic_start>Handle tapsYou not only want to display information to users,you want users to interact with your app.Use the GestureDetector widget to respondto fundamental actions, such as tapping and dragging.info Note  To learn more, watch this short Widget of the Week video on the GestureDetector widget:This recipe shows how to make a custom button that showsa snackbar when tapped with the following steps:<code_start>// The GestureDetector wraps the button.GestureDetector(  // When the child is tapped, show a snackbar.  onTap: () {    const snackBar = SnackBar(content: Text('Tap'));    ScaffoldMessenger.of(context).showSnackBar(snackBar);  },  // The custom button  child: Container(    padding: const EdgeInsets.all(12),    decoration: BoxDecoration(      color: Colors.lightBlue,      borderRadius: BorderRadius.circular(8),    ),    child: const Text('My Button'),  ),)<code_end><topic_end><topic_start>Notes<topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const title = 'Gesture Demo';    return const MaterialApp(      title: title,      home: MyHomePage(title: title),    );  }}class MyHomePage extends StatelessWidget {  final String title;  const MyHomePage({super.key, required this.title});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text(title),      ),      body: const Center(        child: MyButton(),      ),    );  }}class MyButton extends StatelessWidget {  const MyButton({super.key});  @override  Widget build(BuildContext context) {    // The GestureDetector wraps the button.    return GestureDetector(      // When the child is tapped, show a snackbar.      onTap: () {        const snackBar = SnackBar(content: Text('Tap'));        ScaffoldMessenger.of(context).showSnackBar(snackBar);      },      // The custom button      child: Container(        padding: const EdgeInsets.all(12),        decoration: BoxDecoration(          color: Colors.lightBlue,          borderRadius: BorderRadius.circular(8),        ),        child: const Text('My Button'),      ),    );  }}<code_end><topic_end><topic_start>Drag outside an appYou might want to implementdrag and drop somewhere in your app.You have a couple potential approachesthat you can take. One directly usesFlutter widgets and the other uses a package(super_drag_and_drop), available on pub.dev.<topic_end><topic_start>Create draggable widgets within your appIf you want to implement drag and drop withinyour application, you can use the Draggablewidget. For insight into this approach, seethe Drag a UI element within an app recipe.An advantage of using Draggable and DragTarget isthat you can supply Dart code to decide whether to accept a drop.For more information, check out theDraggable widget of the week video.<topic_end><topic_start>Implement drag and drop between appsIf you want to implement drag and drop withinyour application and also between yourapplication and another (possibly non-Flutter) app,check out the super_drag_and_drop package.To avoid implementing two styles of drag and drop,one for drags outside of the app and another fordragging inside the app,you can supply local data to the package toperform drags within your app.Another difference between this approach andusing Draggable directly,is that you must tell the package up frontwhat data your app accepts because the platformAPIs need a synchronous response, which doesn’tallow an asynchronous response from the framework.An advantage of using this approach is that itworks across desktop, mobile, and web.<topic_end><topic_start>Drag a UI elementDrag and drop is a common mobile app interaction.As the user long presses (sometimes called touch & hold)on a widget, another widget appears beneath theuser’s finger, and the user drags the widget to afinal location and releases it.In this recipe, you’ll build a drag-and-drop interactionwhere the user long presses on a choice of food,and then drags that food to the picture of the customer whois paying for it.The following animation shows the app’s behavior:This recipe begins with a prebuilt list of menu items anda row of customers.The first step is to recognize a long pressand display a draggable photo of a menu item.<topic_end><topic_start>Press and dragFlutter provides a widget called LongPressDraggablethat provides the exact behavior that you need to begina drag-and-drop interaction. A LongPressDraggablewidget recognizes when a long press occurs and then displays a new widget near the user’s finger.As the user drags, the widget follows the user’s finger.LongPressDraggable gives you full control over the widget that the user drags.Each menu list item is displayed with a customMenuListItem widget.<code_start>MenuListItem(  name: item.name,  price: item.formattedTotalItemPrice,  photoProvider: item.imageProvider,)<code_end>Wrap the MenuListItem widget with a LongPressDraggable widget.<code_start>LongPressDraggable<Item>(  data: item,  dragAnchorStrategy: pointerDragAnchorStrategy,  feedback: DraggingListItem(    dragKey: _draggableKey,    photoProvider: item.imageProvider,  ),  child: MenuListItem(    name: item.name,    price: item.formattedTotalItemPrice,    photoProvider: item.imageProvider,  ),);<code_end>In this case, when the user long presses on theMenuListItem widget, the LongPressDraggablewidget displays a DraggingListItem.This DraggingListItem displays a photo of theselected food item, centered beneath the user’s finger.The dragAnchorStrategy property is set topointerDragAnchorStrategy.This property value instructs LongPressDraggableto base the DraggableListItem’s position on the user’s finger. As the user moves a finger,the DraggableListItem moves with it.Dragging and dropping is of little use if no informationis transmitted when the item is dropped.For this reason, LongPressDraggable takes a data parameter. In this case, the type of data is Item,which holds information about the food menu item that the user pressed on.The data associated with a LongPressDraggableis sent to a special widget called DragTarget,where the user releases the drag gesture.You’ll implement the drop behavior next.<topic_end><topic_start>Drop the draggableThe user can drop a LongPressDraggable wherever they choose,but dropping the draggable has no effect unless it’s droppedon top of a DragTarget. When the user drops a draggable ontop of a DragTarget widget, the DragTarget widget can either accept or reject the data from the draggable.In this recipe, the user should drop a menu item on aCustomerCart widget to add the menu item to the user’s cart.<code_start>CustomerCart(  hasItems: customer.items.isNotEmpty,  highlighted: candidateItems.isNotEmpty,  customer: customer,);<code_end>Wrap the CustomerCart widget with a DragTarget widget.<code_start>DragTarget<Item>(  builder: (context, candidateItems, rejectedItems) {    return CustomerCart(      hasItems: customer.items.isNotEmpty,      highlighted: candidateItems.isNotEmpty,      customer: customer,    );  },  onAcceptWithDetails: (details) {    _itemDroppedOnCustomerCart(      item: details.data,      customer: customer,    );  },)<code_end>The DragTarget displays your existing widget andalso coordinates with LongPressDraggable to recognizewhen the user drags a draggable on top of the DragTarget.The DragTarget also recognizes when the user dropsa draggable on top of the DragTarget widget.When the user drags a draggable on the DragTarget widget,candidateItems contains the data items that the user is dragging.This draggable allows you to change what your widget lookslike when the user is dragging over it. In this case,the Customer widget turns red whenever any items are dragged above the DragTarget widget. The red visual appearance is configured with the highlighted property within the CustomerCart widget.When the user drops a draggable on the DragTarget widget,the onAcceptWithDetails callback is invoked. This is when you getto decide whether or not to accept the data that was dropped.In this case, the item is always accepted and processed. You might choose to inspect the incoming item to make adifferent decision.Notice that the type of item dropped on DragTargetmust match the type of the item dragged from LongPressDraggable.If the types are not compatible, then the onAcceptWithDetails method isn’t invoked.With a DragTarget widget configured to accept yourdesired data, you can now transmit data from one partof your UI to another by dragging and dropping.In the next step,you update the customer’s cart with the dropped menu item.<topic_end><topic_start>Add a menu item to a cartEach customer is represented by a Customer object,which maintains a cart of items and a price total.<code_start>class Customer {  Customer({    required this.name,    required this.imageProvider,    List<Item>? items,  }) : items = items ?? [];  final String name;  final ImageProvider imageProvider;  final List<Item> items;  String get formattedTotalItemPrice {    final totalPriceCents =        items.fold<int>(0, (prev, item) => prev + item.totalPriceCents);    return '\$${(totalPriceCents / 100.0).toStringAsFixed(2)}';  }}<code_end>The CustomerCart widget displays the customer’s photo,name, total, and item count based on a Customer instance.To update a customer’s cart when a menu item is dropped,add the dropped item to the associated Customer object.<code_start>void _itemDroppedOnCustomerCart({  required Item item,  required Customer customer,}) {  setState(() {    customer.items.add(item);  });}<code_end>The _itemDroppedOnCustomerCart method is invoked inonAcceptWithDetails() when the user drops a menu item on aCustomerCart widget. By adding the dropped item to the customer object, and invoking setState() to cause alayout update, the UI refreshes with the new customer’sprice total and item count.Congratulations! You have a drag-and-drop interactionthat adds food items to a customer’s shopping cart.<topic_end><topic_start>Interactive exampleRun the app:<code_start>import 'package:flutter/material.dart';void main() {  runApp(    const MaterialApp(      home: ExampleDragAndDrop(),      debugShowCheckedModeBanner: false,    ),  );}const List<Item> _items = [  Item(    name: 'Spinach Pizza',    totalPriceCents: 1299,    uid: '1',    imageProvider: NetworkImage('https://flutter'        '.dev/docs/cookbook/img-files/effects/split-check/Food1.jpg'),  ),  Item(    name: 'Veggie Delight',    totalPriceCents: 799,    uid: '2',    imageProvider: NetworkImage('https://flutter'        '.dev/docs/cookbook/img-files/effects/split-check/Food2.jpg'),  ),  Item(    name: 'Chicken Parmesan',    totalPriceCents: 1499,    uid: '3',    imageProvider: NetworkImage('https://flutter'        '.dev/docs/cookbook/img-files/effects/split-check/Food3.jpg'),  ),];@immutableclass ExampleDragAndDrop extends StatefulWidget {  const ExampleDragAndDrop({super.key});  @override  State<ExampleDragAndDrop> createState() => _ExampleDragAndDropState();}class _ExampleDragAndDropState extends State<ExampleDragAndDrop>    with TickerProviderStateMixin {  final List<Customer> _people = [    Customer(      name: 'Makayla',      imageProvider: const NetworkImage('https://flutter'          '.dev/docs/cookbook/img-files/effects/split-check/Avatar1.jpg'),    ),    Customer(      name: 'Nathan',      imageProvider: const NetworkImage('https://flutter'          '.dev/docs/cookbook/img-files/effects/split-check/Avatar2.jpg'),    ),    Customer(      name: 'Emilio',      imageProvider: const NetworkImage('https://flutter'          '.dev/docs/cookbook/img-files/effects/split-check/Avatar3.jpg'),    ),  ];  final GlobalKey _draggableKey = GlobalKey();  void _itemDroppedOnCustomerCart({    required Item item,    required Customer customer,  }) {    setState(() {      customer.items.add(item);    });  }  @override  Widget build(BuildContext context) {    return Scaffold(      backgroundColor: const Color(0xFFF7F7F7),      appBar: _buildAppBar(),      body: _buildContent(),    );  }  PreferredSizeWidget _buildAppBar() {    return AppBar(      iconTheme: const IconThemeData(color: Color(0xFFF64209)),      title: Text(        'Order Food',        style: Theme.of(context).textTheme.headlineMedium?.copyWith(              fontSize: 36,              color: const Color(0xFFF64209),              fontWeight: FontWeight.bold,            ),      ),      backgroundColor: const Color(0xFFF7F7F7),      elevation: 0,    );  }  Widget _buildContent() {    return Stack(      children: [        SafeArea(          child: Column(            children: [              Expanded(                child: _buildMenuList(),              ),              _buildPeopleRow(),            ],          ),        ),      ],    );  }  Widget _buildMenuList() {    return ListView.separated(      padding: const EdgeInsets.all(16),      itemCount: _items.length,      separatorBuilder: (context, index) {        return const SizedBox(          height: 12,        );      },      itemBuilder: (context, index) {        final item = _items[index];        return _buildMenuItem(          item: item,        );      },    );  }  Widget _buildMenuItem({    required Item item,  }) {    return LongPressDraggable<Item>(      data: item,      dragAnchorStrategy: pointerDragAnchorStrategy,      feedback: DraggingListItem(        dragKey: _draggableKey,        photoProvider: item.imageProvider,      ),      child: MenuListItem(        name: item.name,        price: item.formattedTotalItemPrice,        photoProvider: item.imageProvider,      ),    );  }  Widget _buildPeopleRow() {    return Container(      padding: const EdgeInsets.symmetric(        horizontal: 8,        vertical: 20,      ),      child: Row(        children: _people.map(_buildPersonWithDropZone).toList(),      ),    );  }  Widget _buildPersonWithDropZone(Customer customer) {    return Expanded(      child: Padding(        padding: const EdgeInsets.symmetric(          horizontal: 6,        ),        child: DragTarget<Item>(          builder: (context, candidateItems, rejectedItems) {            return CustomerCart(              hasItems: customer.items.isNotEmpty,              highlighted: candidateItems.isNotEmpty,              customer: customer,            );          },          onAcceptWithDetails: (details) {            _itemDroppedOnCustomerCart(              item: details.data,              customer: customer,            );          },        ),      ),    );  }}class CustomerCart extends StatelessWidget {  const CustomerCart({    super.key,    required this.customer,    this.highlighted = false,    this.hasItems = false,  });  final Customer customer;  final bool highlighted;  final bool hasItems;  @override  Widget build(BuildContext context) {    final textColor = highlighted ? Colors.white : Colors.black;    return Transform.scale(      scale: highlighted ? 1.075 : 1.0,      child: Material(        elevation: highlighted ? 8 : 4,        borderRadius: BorderRadius.circular(22),        color: highlighted ? const Color(0xFFF64209) : Colors.white,        child: Padding(          padding: const EdgeInsets.symmetric(            horizontal: 12,            vertical: 24,          ),          child: Column(            mainAxisSize: MainAxisSize.min,            children: [              ClipOval(                child: SizedBox(                  width: 46,                  height: 46,                  child: Image(                    image: customer.imageProvider,                    fit: BoxFit.cover,                  ),                ),              ),              const SizedBox(height: 8),              Text(                customer.name,                style: Theme.of(context).textTheme.titleMedium?.copyWith(                      color: textColor,                      fontWeight:                          hasItems ? FontWeight.normal : FontWeight.bold,                    ),              ),              Visibility(                visible: hasItems,                maintainState: true,                maintainAnimation: true,                maintainSize: true,                child: Column(                  children: [                    const SizedBox(height: 4),                    Text(                      customer.formattedTotalItemPrice,                      style: Theme.of(context).textTheme.bodySmall!.copyWith(                            color: textColor,                            fontSize: 16,                            fontWeight: FontWeight.bold,                          ),                    ),                    const SizedBox(height: 4),                    Text(                      '${customer.items.length} item${customer.items.length != 1 ? 's' : ''}',                      style: Theme.of(context).textTheme.titleMedium!.copyWith(                            color: textColor,                            fontSize: 12,                          ),                    ),                  ],                ),              )            ],          ),        ),      ),    );  }}class MenuListItem extends StatelessWidget {  const MenuListItem({    super.key,    this.name = '',    this.price = '',    required this.photoProvider,    this.isDepressed = false,  });  final String name;  final String price;  final ImageProvider photoProvider;  final bool isDepressed;  @override  Widget build(BuildContext context) {    return Material(      elevation: 12,      borderRadius: BorderRadius.circular(20),      child: Padding(        padding: const EdgeInsets.all(12),        child: Row(          mainAxisSize: MainAxisSize.max,          children: [            ClipRRect(              borderRadius: BorderRadius.circular(12),              child: SizedBox(                width: 120,                height: 120,                child: Center(                  child: AnimatedContainer(                    duration: const Duration(milliseconds: 100),                    curve: Curves.easeInOut,                    height: isDepressed ? 115 : 120,                    width: isDepressed ? 115 : 120,                    child: Image(                      image: photoProvider,                      fit: BoxFit.cover,                    ),                  ),                ),              ),            ),            const SizedBox(width: 30),            Expanded(              child: Column(                crossAxisAlignment: CrossAxisAlignment.start,                children: [                  Text(                    name,                    style: Theme.of(context).textTheme.titleMedium?.copyWith(                          fontSize: 18,                        ),                  ),                  const SizedBox(height: 10),                  Text(                    price,                    style: Theme.of(context).textTheme.titleMedium?.copyWith(                          fontWeight: FontWeight.bold,                          fontSize: 18,                        ),                  ),                ],              ),            ),          ],        ),      ),    );  }}class DraggingListItem extends StatelessWidget {  const DraggingListItem({    super.key,    required this.dragKey,    required this.photoProvider,  });  final GlobalKey dragKey;  final ImageProvider photoProvider;  @override  Widget build(BuildContext context) {    return FractionalTranslation(      translation: const Offset(-0.5, -0.5),      child: ClipRRect(        key: dragKey,        borderRadius: BorderRadius.circular(12),        child: SizedBox(          height: 150,          width: 150,          child: Opacity(            opacity: 0.85,            child: Image(              image: photoProvider,              fit: BoxFit.cover,            ),          ),        ),      ),    );  }}@immutableclass Item {  const Item({    required this.totalPriceCents,    required this.name,    required this.uid,    required this.imageProvider,  });  final int totalPriceCents;  final String name;  final String uid;  final ImageProvider imageProvider;  String get formattedTotalItemPrice =>      '\$${(totalPriceCents / 100.0).toStringAsFixed(2)}';}class Customer {  Customer({    required this.name,    required this.imageProvider,    List<Item>? items,  }) : items = items ?? [];  final String name;  final ImageProvider imageProvider;  final List<Item> items;  String get formattedTotalItemPrice {    final totalPriceCents =        items.fold<int>(0, (prev, item) => prev + item.totalPriceCents);    return '\$${(totalPriceCents / 100.0).toStringAsFixed(2)}';  }}<code_end><topic_end><topic_start>Add Material touch ripplesWidgets that follow the Material Design guidelines displaya ripple animation when tapped.Flutter provides the InkWellwidget to perform this effect.Create a ripple effect using the following steps:<code_start>// The InkWell wraps the custom flat button widget.InkWell(  // When the user taps the button, show a snackbar.  onTap: () {    ScaffoldMessenger.of(context).showSnackBar(const SnackBar(      content: Text('Tap'),    ));  },  child: const Padding(    padding: EdgeInsets.all(12),    child: Text('Flat Button'),  ),)<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const title = 'InkWell Demo';    return const MaterialApp(      title: title,      home: MyHomePage(title: title),    );  }}class MyHomePage extends StatelessWidget {  final String title;  const MyHomePage({super.key, required this.title});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text(title),      ),      body: const Center(        child: MyButton(),      ),    );  }}class MyButton extends StatelessWidget {  const MyButton({super.key});  @override  Widget build(BuildContext context) {    // The InkWell wraps the custom flat button widget.    return InkWell(      // When the user taps the button, show a snackbar.      onTap: () {        ScaffoldMessenger.of(context).showSnackBar(const SnackBar(          content: Text('Tap'),        ));      },      child: const Padding(        padding: EdgeInsets.all(12),        child: Text('Flat Button'),      ),    );  }}<code_end><topic_end><topic_start>Implement swipe to dismissThe “swipe to dismiss” pattern is common in many mobile apps.For example, when writing an email app,you might want to allow a user to swipe awayemail messages to delete them from a list.Flutter makes this task easy by providing theDismissible widget.Learn how to implement swipe to dismiss with the following steps:<topic_end><topic_start>1. Create a list of itemsFirst, create a list of items. For detailedinstructions on how to create a list,follow the Working with long lists recipe.<topic_end><topic_start>Create a data sourceIn this example,you want 20 sample items to work with.To keep it simple, generate a list of strings.<code_start>final items = List<String>.generate(20, (i) => 'Item ${i + 1}');<code_end><topic_end><topic_start>Convert the data source into a listDisplay each item in the list on screen. Users won’tbe able to swipe these items away just yet.<code_start>ListView.builder(  itemCount: items.length,  itemBuilder: (context, index) {    return ListTile(      title: Text(items[index]),    );  },)<code_end><topic_end><topic_start>2. Wrap each item in a Dismissible widgetIn this step,give users the ability to swipe an item off the list by using theDismissible widget.After the user has swiped away the item,remove the item from the list and display a snackbar.In a real app, you might need to perform more complex logic,such as removing the item from a web service or database.Update the itemBuilder() function to return a Dismissible widget:<code_start>itemBuilder: (context, index) {  final item = items[index];  return Dismissible(    // Each Dismissible must contain a Key. Keys allow Flutter to    // uniquely identify widgets.    key: Key(item),    // Provide a function that tells the app    // what to do after an item has been swiped away.    onDismissed: (direction) {      // Remove the item from the data source.      setState(() {        items.removeAt(index);      });      // Then show a snackbar.      ScaffoldMessenger.of(context)          .showSnackBar(SnackBar(content: Text('$item dismissed')));    },    child: ListTile(      title: Text(item),    ),  );},<code_end><topic_end><topic_start>3. Provide “leave behind” indicatorsAs it stands,the app allows users to swipe items off the list, but it doesn’tgive a visual indication of what happens when they do.To provide a cue that items are removed,display a “leave behind” indicator as theyswipe the item off the screen. In this case,the indicator is a red background.To add the indicator,provide a background parameter to the Dismissible.<topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() {  runApp(const MyApp());}// MyApp is a StatefulWidget. This allows updating the state of the// widget when an item is removed.class MyApp extends StatefulWidget {  const MyApp({super.key});  @override  MyAppState createState() {    return MyAppState();  }}class MyAppState extends State<MyApp> {  final items = List<String>.generate(20, (i) => 'Item ${i + 1}');  @override  Widget build(BuildContext context) {    const title = 'Dismissing Items';    return MaterialApp(      title: title,      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: Scaffold(        appBar: AppBar(          title: const Text(title),        ),        body: ListView.builder(          itemCount: items.length,          itemBuilder: (context, index) {            final item = items[index];            return Dismissible(              // Each Dismissible must contain a Key. Keys allow Flutter to              // uniquely identify widgets.              key: Key(item),              // Provide a function that tells the app              // what to do after an item has been swiped away.              onDismissed: (direction) {                // Remove the item from the data source.                setState(() {                  items.removeAt(index);                });                // Then show a snackbar.                ScaffoldMessenger.of(context)                    .showSnackBar(SnackBar(content: Text('$item dismissed')));              },              // Show a red background as the item is swiped away.              background: Container(color: Colors.red),              child: ListTile(                title: Text(item),              ),            );          },        ),      ),    );  }}<code_end><topic_end><topic_start>Input & forms<topic_end><topic_start>Topics<topic_end><topic_start>Create and style a text fieldText fields allow users to type text into an app.They are used to build forms,send messages, create search experiences, and more.In this recipe, explore how to create and style text fields.Flutter provides two text fields:TextField and TextFormField.<topic_end><topic_start>TextFieldTextField is the most commonly used text input widget.By default, a TextField is decorated with an underline.You can add a label, icon, inline hint text, and error text by supplying anInputDecoration as the decorationproperty of the TextField.To remove the decoration entirely (including theunderline and the space reserved for the label),set the decoration to null.<code_start>TextField(  decoration: InputDecoration(    border: OutlineInputBorder(),    hintText: 'Enter a search term',  ),),<code_end>To retrieve the value when it changes,see the Handle changes to a text field recipe.<topic_end><topic_start>TextFormFieldTextFormField wraps a TextField and integrates itwith the enclosing Form.This provides additional functionality,such as validation and integration with otherFormField widgets.<code_start>TextFormField(  decoration: const InputDecoration(    border: UnderlineInputBorder(),    labelText: 'Enter your username',  ),),<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const appTitle = 'Form Styling Demo';    return MaterialApp(      title: appTitle,      home: Scaffold(        appBar: AppBar(          title: const Text(appTitle),        ),        body: const MyCustomForm(),      ),    );  }}class MyCustomForm extends StatelessWidget {  const MyCustomForm({super.key});  @override  Widget build(BuildContext context) {    return Column(      crossAxisAlignment: CrossAxisAlignment.start,      children: <Widget>[        const Padding(          padding: EdgeInsets.symmetric(horizontal: 8, vertical: 16),          child: TextField(            decoration: InputDecoration(              border: OutlineInputBorder(),              hintText: 'Enter a search term',            ),          ),        ),        Padding(          padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),          child: TextFormField(            decoration: const InputDecoration(              border: UnderlineInputBorder(),              labelText: 'Enter your username',            ),          ),        ),      ],    );  }}<code_end>For more information on input validation, see theBuilding a form with validation recipe.<topic_end><topic_start>Retrieve the value of a text fieldIn this recipe,learn how to retrieve the text a user has entered into a text fieldusing the following steps:<topic_end><topic_start>1. Create a TextEditingControllerTo retrieve the text a user has entered into a text field,create a TextEditingControllerand supply it to a TextField or TextFormField.Important: Call dispose of the TextEditingController when  you’ve finished using it. This ensures that you discard any resources  used by the object.<code_start>// Define a custom Form widget.class MyCustomForm extends StatefulWidget {  const MyCustomForm({super.key});  @override  State<MyCustomForm> createState() => _MyCustomFormState();}// Define a corresponding State class.// This class holds the data related to the Form.class _MyCustomFormState extends State<MyCustomForm> {  // Create a text controller and use it to retrieve the current value  // of the TextField.  final myController = TextEditingController();  @override  void dispose() {    // Clean up the controller when the widget is disposed.    myController.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    // Fill this out in the next step.  }}<code_end><topic_end><topic_start>2. Supply the TextEditingController to a TextFieldNow that you have a TextEditingController, wire it upto a text field using the controller property:<code_start>return TextField(  controller: myController,);<code_end><topic_end><topic_start>3. Display the current value of the text fieldAfter supplying the TextEditingController to the text field,begin reading values. Use the text()method provided by the TextEditingController to retrieve theString that the user has entered into the text field.The following code displays an alert dialog with the currentvalue of the text field when the user taps a floating action button.<code_start>FloatingActionButton(  // When the user presses the button, show an alert dialog containing  // the text that the user has entered into the text field.  onPressed: () {    showDialog(      context: context,      builder: (context) {        return AlertDialog(          // Retrieve the text that the user has entered by using the          // TextEditingController.          content: Text(myController.text),        );      },    );  },  tooltip: 'Show me the value!',  child: const Icon(Icons.text_fields),),<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Retrieve Text Input',      home: MyCustomForm(),    );  }}// Define a custom Form widget.class MyCustomForm extends StatefulWidget {  const MyCustomForm({super.key});  @override  State<MyCustomForm> createState() => _MyCustomFormState();}// Define a corresponding State class.// This class holds the data related to the Form.class _MyCustomFormState extends State<MyCustomForm> {  // Create a text controller and use it to retrieve the current value  // of the TextField.  final myController = TextEditingController();  @override  void dispose() {    // Clean up the controller when the widget is disposed.    myController.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Retrieve Text Input'),      ),      body: Padding(        padding: const EdgeInsets.all(16),        child: TextField(          controller: myController,        ),      ),      floatingActionButton: FloatingActionButton(        // When the user presses the button, show an alert dialog containing        // the text that the user has entered into the text field.        onPressed: () {          showDialog(            context: context,            builder: (context) {              return AlertDialog(                // Retrieve the text the that user has entered by using the                // TextEditingController.                content: Text(myController.text),              );            },          );        },        tooltip: 'Show me the value!',        child: const Icon(Icons.text_fields),      ),    );  }}<code_end><topic_end><topic_start>Handle changes to a text fieldIn some cases, it’s useful to run a callback function every time the textin a text field changes. For example, you might want to build a searchscreen with autocomplete functionality where you want to update theresults as the user types.How do you run a callback function every time the text changes?With Flutter, you have two options:<topic_end><topic_start>1. Supply an onChanged() callback to a TextField or a TextFormFieldThe simplest approach is to supply an onChanged() callback to aTextField or a TextFormField.Whenever the text changes, the callback is invoked.In this example, print the current value and length of the text field to the console every time the text changes.It’s important to use characters when dealing with user input,as text may contain complex characters.This ensures that every character is counted correctlyas they appear to the user.<code_start>TextField(  onChanged: (text) {    print('First text field: $text (${text.characters.length})');  },),<code_end><topic_end><topic_start>2. Use a TextEditingControllerA more powerful, but more elaborate approach, is to supply aTextEditingController as the controllerproperty of the TextField or a TextFormField.To be notified when the text changes, listen to the controllerusing the addListener() method using the following steps:<topic_end><topic_start>Create a TextEditingControllerCreate a TextEditingController:<code_start>// Define a custom Form widget.class MyCustomForm extends StatefulWidget {  const MyCustomForm({super.key});  @override  State<MyCustomForm> createState() => _MyCustomFormState();}// Define a corresponding State class.// This class holds data related to the Form.class _MyCustomFormState extends State<MyCustomForm> {  // Create a text controller. Later, use it to retrieve the  // current value of the TextField.  final myController = TextEditingController();  @override  void dispose() {    // Clean up the controller when the widget is removed from the    // widget tree.    myController.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    // Fill this out in the next step.  }}<code_end>info Note  Remember to dispose of the TextEditingController when it’s no  longer needed. This ensures that you discard any resources used  by the object.<topic_end><topic_start>Connect the TextEditingController to a text fieldSupply the TextEditingController to either a TextFieldor a TextFormField. Once you wire these two classes together,you can begin listening for changes to the text field.<code_start>TextField(  controller: myController,),<code_end><topic_end><topic_start>Create a function to print the latest valueYou need a function to run every time the text changes.Create a method in the _MyCustomFormState class that printsout the current value of the text field.<code_start>void _printLatestValue() {  final text = myController.text;  print('Second text field: $text (${text.characters.length})');}<code_end><topic_end><topic_start>Listen to the controller for changesFinally, listen to the TextEditingController and call the_printLatestValue() method when the text changes. Use theaddListener() method for this purpose.Begin listening for changes when the_MyCustomFormState class is initialized,and stop listening when the _MyCustomFormState is disposed.<code_start>@overridevoid initState() {  super.initState();  // Start listening to changes.  myController.addListener(_printLatestValue);}<code_end><code_start>@overridevoid dispose() {  // Clean up the controller when the widget is removed from the widget tree.  // This also removes the _printLatestValue listener.  myController.dispose();  super.dispose();}<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Retrieve Text Input',      home: MyCustomForm(),    );  }}// Define a custom Form widget.class MyCustomForm extends StatefulWidget {  const MyCustomForm({super.key});  @override  State<MyCustomForm> createState() => _MyCustomFormState();}// Define a corresponding State class.// This class holds data related to the Form.class _MyCustomFormState extends State<MyCustomForm> {  // Create a text controller and use it to retrieve the current value  // of the TextField.  final myController = TextEditingController();  @override  void initState() {    super.initState();    // Start listening to changes.    myController.addListener(_printLatestValue);  }  @override  void dispose() {    // Clean up the controller when the widget is removed from the widget tree.    // This also removes the _printLatestValue listener.    myController.dispose();    super.dispose();  }  void _printLatestValue() {    final text = myController.text;    print('Second text field: $text (${text.characters.length})');  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Retrieve Text Input'),      ),      body: Padding(        padding: const EdgeInsets.all(16),        child: Column(          children: [            TextField(              onChanged: (text) {                print('First text field: $text (${text.characters.length})');              },            ),            TextField(              controller: myController,            ),          ],        ),      ),    );  }}<code_end><topic_end><topic_start>Focus and text fieldsWhen a text field is selected and accepting input,it is said to have “focus.”Generally, users shift focus to a text field by tapping,and developers shift focus to a text field programmatically byusing the tools described in this recipe.Managing focus is a fundamental tool for creating forms with an intuitiveflow. For example, say you have a search screen with a text field.When the user navigates to the search screen,you can set the focus to the text field for the search term.This allows the user to start typing as soon as the screenis visible, without needing to manually tap the text field.In this recipe, learn how to give the focusto a text field as soon as it’s visible,as well as how to give focus to a text fieldwhen a button is tapped.<topic_end><topic_start>Focus a text field as soon as it’s visibleTo give focus to a text field as soon as it’s visible,use the autofocus property.For more information on handling input and creating text fields,see the Forms section of the cookbook.<topic_end><topic_start>Focus a text field when a button is tappedRather than immediately shifting focus to a specific text field,you might need to give focus to a text field at a later point in time.In the real world, you might also need to give focus to a specifictext field in response to an API call or a validation error.In this example, give focus to a text field after the userpresses a button using the following steps:<topic_end><topic_start>1. Create a FocusNodeFirst, create a FocusNode.Use the FocusNode to identify a specific TextField in Flutter’s“focus tree.” This allows you to give focus to the TextFieldin the next steps.Since focus nodes are long-lived objects, manage the lifecycleusing a State object. Use the following instructions to createa FocusNode instance inside the initState() method of aState class, and clean it up in the dispose() method:<code_start>// Define a custom Form widget.class MyCustomForm extends StatefulWidget {  const MyCustomForm({super.key});  @override  State<MyCustomForm> createState() => _MyCustomFormState();}// Define a corresponding State class.// This class holds data related to the form.class _MyCustomFormState extends State<MyCustomForm> {  // Define the focus node. To manage the lifecycle, create the FocusNode in  // the initState method, and clean it up in the dispose method.  late FocusNode myFocusNode;  @override  void initState() {    super.initState();    myFocusNode = FocusNode();  }  @override  void dispose() {    // Clean up the focus node when the Form is disposed.    myFocusNode.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    // Fill this out in the next step.  }}<code_end><topic_end><topic_start>2. Pass the FocusNode to a TextFieldNow that you have a FocusNode,pass it to a specific TextField in the build() method.<code_start>@overrideWidget build(BuildContext context) {  return TextField(    focusNode: myFocusNode,  );}<code_end><topic_end><topic_start>3. Give focus to the TextField when a button is tappedFinally, focus the text field when the user taps a floatingaction button. Use the requestFocus() method to performthis task.<code_start>FloatingActionButton(  // When the button is pressed,  // give focus to the text field using myFocusNode.  onPressed: () => myFocusNode.requestFocus(),),<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Text Field Focus',      home: MyCustomForm(),    );  }}// Define a custom Form widget.class MyCustomForm extends StatefulWidget {  const MyCustomForm({super.key});  @override  State<MyCustomForm> createState() => _MyCustomFormState();}// Define a corresponding State class.// This class holds data related to the form.class _MyCustomFormState extends State<MyCustomForm> {  // Define the focus node. To manage the lifecycle, create the FocusNode in  // the initState method, and clean it up in the dispose method.  late FocusNode myFocusNode;  @override  void initState() {    super.initState();    myFocusNode = FocusNode();  }  @override  void dispose() {    // Clean up the focus node when the Form is disposed.    myFocusNode.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Text Field Focus'),      ),      body: Padding(        padding: const EdgeInsets.all(16),        child: Column(          children: [            // The first text field is focused on as soon as the app starts.            const TextField(              autofocus: true,            ),            // The second text field is focused on when a user taps the            // FloatingActionButton.            TextField(              focusNode: myFocusNode,            ),          ],        ),      ),      floatingActionButton: FloatingActionButton(        // When the button is pressed,        // give focus to the text field using myFocusNode.        onPressed: () => myFocusNode.requestFocus(),        tooltip: 'Focus Second Text Field',        child: const Icon(Icons.edit),      ), // This trailing comma makes auto-formatting nicer for build methods.    );  }}<code_end><topic_end><topic_start>Build a form with validationApps often require users to enter information into a text field.For example, you might require users to log in with an email addressand password combination.To make apps secure and easy to use, check whether theinformation the user has provided is valid. If the user has correctly filledout the form, process the information. If the user submits incorrectinformation, display a friendly error message letting them know what wentwrong.In this example, learn how to add validation to a form that hasa single text field using the following steps:<topic_end><topic_start>1. Create a Form with a GlobalKeyCreate a Form.The Form widget acts as a container for grouping andvalidating multiple form fields.When creating the form, provide a GlobalKey.This assigns a unique identifier to your Form.It also allows you to validate the form later.Create the form as a StatefulWidget.This allows you to create a unique GlobalKey<FormState>() once.You can then store it as a variable and access it at different points.If you made this a StatelessWidget, you’d need to store this key somewhere.As it is resource expensive, you wouldn’t want to generate a newGlobalKey each time you run the build method.<code_start>import 'package:flutter/material.dart';// Define a custom Form widget.class MyCustomForm extends StatefulWidget {  const MyCustomForm({super.key});  @override  MyCustomFormState createState() {    return MyCustomFormState();  }}// Define a corresponding State class.// This class holds data related to the form.class MyCustomFormState extends State<MyCustomForm> {  // Create a global key that uniquely identifies the Form widget  // and allows validation of the form.  //  // Note: This is a `GlobalKey<FormState>`,  // not a GlobalKey<MyCustomFormState>.  final _formKey = GlobalKey<FormState>();  @override  Widget build(BuildContext context) {    // Build a Form widget using the _formKey created above.    return Form(      key: _formKey,      child: const Column(        children: <Widget>[          // Add TextFormFields and ElevatedButton here.        ],      ),    );  }}<code_end>lightbulb Tip  Using a GlobalKey is the recommended way to access a form.  However, if you have a more complex widget tree,  you can use the Form.of() method to  access the form within nested widgets.<topic_end><topic_start>2. Add a TextFormField with validation logicAlthough the Form is in place,it doesn’t have a way for users to enter text.That’s the job of a TextFormField.The TextFormField widget renders a material design text fieldand can display validation errors when they occur.Validate the input by providing a validator() function to theTextFormField. If the user’s input isn’t valid,the validator function returns a String containingan error message.If there are no errors, the validator must return null.For this example, create a validator that ensures theTextFormField isn’t empty. If it is empty,return a friendly error message.<code_start>TextFormField(  // The validator receives the text that the user has entered.  validator: (value) {    if (value == null || value.isEmpty) {      return 'Please enter some text';    }    return null;  },),<code_end><topic_end><topic_start>3. Create a button to validate and submit the formNow that you have a form with a text field,provide a button that the user can tap to submit the information.When the user attempts to submit the form, check if the form is valid.If it is, display a success message.If it isn’t (the text field has no content) display the error message.<code_start>ElevatedButton(  onPressed: () {    // Validate returns true if the form is valid, or false otherwise.    if (_formKey.currentState!.validate()) {      // If the form is valid, display a snackbar. In the real world,      // you'd often call a server or save the information in a database.      ScaffoldMessenger.of(context).showSnackBar(        const SnackBar(content: Text('Processing Data')),      );    }  },  child: const Text('Submit'),),<code_end><topic_end><topic_start>How does this work?To validate the form, use the _formKey created instep 1. You can use the _formKey.currentState()method to access the FormState,which is automatically created by Flutter when building a Form.The FormState class contains the validate() method.When the validate() method is called, it runs the validator()function for each text field in the form.If everything looks good, the validate() method returns true.If any text field contains errors, the validate() methodrebuilds the form to display any error messages and returns false.<topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const appTitle = 'Form Validation Demo';    return MaterialApp(      title: appTitle,      home: Scaffold(        appBar: AppBar(          title: const Text(appTitle),        ),        body: const MyCustomForm(),      ),    );  }}// Create a Form widget.class MyCustomForm extends StatefulWidget {  const MyCustomForm({super.key});  @override  MyCustomFormState createState() {    return MyCustomFormState();  }}// Create a corresponding State class.// This class holds data related to the form.class MyCustomFormState extends State<MyCustomForm> {  // Create a global key that uniquely identifies the Form widget  // and allows validation of the form.  //  // Note: This is a GlobalKey<FormState>,  // not a GlobalKey<MyCustomFormState>.  final _formKey = GlobalKey<FormState>();  @override  Widget build(BuildContext context) {    // Build a Form widget using the _formKey created above.    return Form(      key: _formKey,      child: Column(        crossAxisAlignment: CrossAxisAlignment.start,        children: [          TextFormField(            // The validator receives the text that the user has entered.            validator: (value) {              if (value == null || value.isEmpty) {                return 'Please enter some text';              }              return null;            },          ),          Padding(            padding: const EdgeInsets.symmetric(vertical: 16),            child: ElevatedButton(              onPressed: () {                // Validate returns true if the form is valid, or false otherwise.                if (_formKey.currentState!.validate()) {                  // If the form is valid, display a snackbar. In the real world,                  // you'd often call a server or save the information in a database.                  ScaffoldMessenger.of(context).showSnackBar(                    const SnackBar(content: Text('Processing Data')),                  );                }              },              child: const Text('Submit'),            ),          ),        ],      ),    );  }}<code_end>To learn how to retrieve these values, check out theRetrieve the value of a text field recipe.<topic_end><topic_start>Display a snackbarIt can be useful to briefly inform your users when certain actionstake place. For example, when a user swipes away a message in a list,you might want to inform them that the message has been deleted.You might even want to give them an option to undo the action.In Material Design, this is the job of a SnackBar.This recipe implements a snackbar using the following steps:<topic_end><topic_start>1. Create a ScaffoldWhen creating apps that follow the Material Design guidelines,give your apps a consistent visual structure.In this example, display the SnackBar at the bottom of the screen,without overlapping other importantwidgets, such as the FloatingActionButton.The Scaffold widget, from the material library,creates this visual structure and ensures that importantwidgets don’t overlap.<code_start>return MaterialApp(  title: 'SnackBar Demo',  home: Scaffold(    appBar: AppBar(      title: const Text('SnackBar Demo'),    ),    body: const SnackBarPage(),  ),);<code_end><topic_end><topic_start>2. Display a SnackBarWith the Scaffold in place, display a SnackBar.First, create a SnackBar, then display it using ScaffoldMessenger.<code_start>const snackBar = SnackBar(  content: Text('Yay! A SnackBar!'),);// Find the ScaffoldMessenger in the widget tree// and use it to show a SnackBar.ScaffoldMessenger.of(context).showSnackBar(snackBar);<code_end>info Note  To learn more, watch this short Widget of the Week video on the ScaffoldMessenger widget:<topic_end><topic_start>3. Provide an optional actionYou might want to provide an action to the user whenthe SnackBar is displayed.For example, if the user accidentally deletes a message,they might use an optional action in the SnackBar to recoverthe message.Here’s an example of providingan additional action to the SnackBar widget:<code_start>final snackBar = SnackBar(  content: const Text('Yay! A SnackBar!'),  action: SnackBarAction(    label: 'Undo',    onPressed: () {      // Some code to undo the change.    },  ),);<code_end><topic_end><topic_start>Interactive exampleinfo Note  In this example, the SnackBar displays when a user taps a button.  For more information on working with user input,  see the Gestures section of the cookbook.<code_start>import 'package:flutter/material.dart';void main() => runApp(const SnackBarDemo());class SnackBarDemo extends StatelessWidget {  const SnackBarDemo({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'SnackBar Demo',      home: Scaffold(        appBar: AppBar(          title: const Text('SnackBar Demo'),        ),        body: const SnackBarPage(),      ),    );  }}class SnackBarPage extends StatelessWidget {  const SnackBarPage({super.key});  @override  Widget build(BuildContext context) {    return Center(      child: ElevatedButton(        onPressed: () {          final snackBar = SnackBar(            content: const Text('Yay! A SnackBar!'),            action: SnackBarAction(              label: 'Undo',              onPressed: () {                // Some code to undo the change.              },            ),          );          // Find the ScaffoldMessenger in the widget tree          // and use it to show a SnackBar.          ScaffoldMessenger.of(context).showSnackBar(snackBar);        },        child: const Text('Show SnackBar'),      ),    );  }}<code_end><topic_end><topic_start>Using Actions and ShortcutsThis page describes how to bind physical keyboard events to actions in the userinterface. For instance, to define keyboard shortcuts in your application, thispage is for you.<topic_end><topic_start>OverviewFor a GUI application to do anything, it has to have actions: users want to tellthe application to do something. Actions are often simple functions thatdirectly perform the action (such as set a value or save a file). In a largerapplication, however, things are more complex: the code for invoking the action,and the code for the action itself might need to be in different places.Shortcuts (key bindings) might need definition at a level that knows nothingabout the actions they invoke.That’s where Flutter’s actions and shortcuts system comes in. It allowsdevelopers to define actions that fulfill intents bound to them. In thiscontext, an intent is a generic action that the user wishes to perform, and anIntent class instance represents these user intents in Flutter. AnIntent can be general purpose, fulfilled by different actions in differentcontexts. An Action can be a simple callback (as in the case ofthe CallbackAction) or something more complex that integrates with entireundo/redo architectures (for example) or other logic.Shortcuts are key bindings that activate by pressing a key or combinationof keys. The key combinations reside in a table with their bound intent. Whenthe Shortcuts widget invokes them, it sends their matching intent to theactions subsystem for fulfillment.To illustrate the concepts in actions and shortcuts, this article creates asimple app that allows a user to select and copy text in a text field using bothbuttons and shortcuts.<topic_end><topic_start>Why separate Actions from Intents?You might wonder: why not just map a key combination directly to an action?  Whyhave intents at all? This is because it is useful to have a separation ofconcerns between where the key mapping definitions are (often at a high level),and where the action definitions are (often at a low level), and because it isimportant to be able to have a single key combination map to an intendedoperation in an app, and have it adapt automatically to whichever actionfulfills that intended operation for the focused context.For instance, Flutter has an ActivateIntent widget that maps each type ofcontrol to its corresponding version of an ActivateAction (and that executesthe code that activates the control). This code often needs fairly privateaccess to do its work. If the extra layer of indirection that Intents providedidn’t exist, it would be necessary to elevate the definition of the actions towhere the defining instance of the Shortcuts widget could see them, causingthe shortcuts to have more knowledge than necessary about which action toinvoke, and to have access to or provide state that it wouldn’t necessarily haveor need otherwise. This allows your code to separate the two concerns to be moreindependent.Intents configure an action so that the same action can serve multiple uses. Anexample of this is DirectionalFocusIntent, which takes a direction to movethe focus in, allowing the DirectionalFocusAction to know which direction tomove the focus. Just be careful: don’t pass state in the Intent that appliesto all  invocations of an Action: that kind of state should be passed to theconstructor of the Action itself, to keep the Intent from needing to knowtoo much.<topic_end><topic_start>Why not use callbacks?You also might wonder: why not just use a callback instead of an Actionobject? The main reason is that it’s useful for actions to decide whether theyare enabled by implementing isEnabled. Also, it is often helpful if the keybindings, and the implementation of those bindings, are in different places.If all you need are callbacks without the flexibility of Actions andShortcuts, you can use the CallbackShortcuts widget:<code_start>@overrideWidget build(BuildContext context) {  return CallbackShortcuts(    bindings: <ShortcutActivator, VoidCallback>{      const SingleActivator(LogicalKeyboardKey.arrowUp): () {        setState(() => count = count + 1);      },      const SingleActivator(LogicalKeyboardKey.arrowDown): () {        setState(() => count = count - 1);      },    },    child: Focus(      autofocus: true,      child: Column(        children: <Widget>[          const Text('Press the up arrow key to add to the counter'),          const Text('Press the down arrow key to subtract from the counter'),          Text('count: $count'),        ],      ),    ),  );}<code_end><topic_end><topic_start>ShortcutsAs you’ll see below, actions are useful on their own, but the most common usecase involves binding them to a keyboard shortcut. This is what the Shortcutswidget is for.It is inserted into the widget hierarchy to define key combinations thatrepresent the user’s intent when that key combination is pressed. To convertthat intended purpose for the key combination into a concrete action, theActions widget used to map the Intent to an Action. For instance, you candefine a SelectAllIntent, and bind it to your own SelectAllAction or to yourCanvasSelectAllAction, and from that one key binding, the system invokeseither one, depending on which part of your application has focus. Let’s see howthe key binding part works:<code_start>@overrideWidget build(BuildContext context) {  return Shortcuts(    shortcuts: <LogicalKeySet, Intent>{      LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyA):          const SelectAllIntent(),    },    child: Actions(      dispatcher: LoggingActionDispatcher(),      actions: <Type, Action<Intent>>{        SelectAllIntent: SelectAllAction(model),      },      child: Builder(        builder: (context) => TextButton(          onPressed: Actions.handler<SelectAllIntent>(            context,            const SelectAllIntent(),          ),          child: const Text('SELECT ALL'),        ),      ),    ),  );}<code_end>The map given to a Shortcuts widget maps a LogicalKeySet (or aShortcutActivator, see note below) to an Intent instance. The logical keyset defines a set of one or more keys, and the intent indicates the intendedpurpose of the keypress. The Shortcuts widget looks up key presses in the map,to find an Intent instance, which it gives to the action’s invoke() method.info NoteShortcutActivator is a replacement for LogicalKeySet.  It allows for more flexible and correct activation of shortcuts.  LogicalKeySet is a ShortcutActivator, of course, but  there is also SingleActivator, which takes a single key and the  optional modifiers to be pressed before the key.  Then there is CharacterActivator, which activates a shortcut based on the  character produced by a key sequence, instead of the logical keys themselves.  ShortcutActivator is also meant to be subclassed to allow for  custom ways of activating shortcuts from key events.<topic_end><topic_start>The ShortcutManagerThe shortcut manager, a longer-lived object than the Shortcuts widget, passeson key events when it receives them. It contains the logic for deciding how tohandle the keys, the logic for walking up the tree to find other shortcutmappings, and maintains a map of key combinations to intents.While the default behavior of the ShortcutManager is usually desirable, theShortcuts widget takes a ShortcutManager that you can subclass to customizeits functionality.For example, if you wanted to log each key that a Shortcuts widget handled,you could make a LoggingShortcutManager:<code_start>class LoggingShortcutManager extends ShortcutManager {  @override  KeyEventResult handleKeypress(BuildContext context, KeyEvent event) {    final KeyEventResult result = super.handleKeypress(context, event);    if (result == KeyEventResult.handled) {      print('Handled shortcut $event in $context');    }    return result;  }}<code_end>Now, every time the Shortcuts widget handles a shortcut, it prints out the keyevent and relevant context.<topic_end><topic_start>ActionsActions allow for the definition of operations that the application canperform by invoking them with an Intent. Actions can be enabled or disabled,and receive the intent instance that invoked them as an argument to allowconfiguration by the intent.<topic_end><topic_start>Defining actionsActions, in their simplest form, are just subclasses of Action<Intent> with aninvoke() method. Here’s a simple action that simply invokes a function on theprovided model:<code_start>class SelectAllAction extends Action<SelectAllIntent> {  SelectAllAction(this.model);  final Model model;  @override  void invoke(covariant SelectAllIntent intent) => model.selectAll();}<code_end>Or, if it’s too much of a bother to create a new class, use a CallbackAction:<code_start>CallbackAction(onInvoke: (intent) => model.selectAll());<code_end>Once you have an action, you add it to your application using the Actionswidget, which takes a map of Intent types to Actions:<code_start>@overrideWidget build(BuildContext context) {  return Actions(    actions: <Type, Action<Intent>>{      SelectAllIntent: SelectAllAction(model),    },    child: child,  );}<code_end>The Shortcuts widget uses the Focus widget’s context and Actions.invoke tofind which action to invoke. If the Shortcuts widget doesn’t find a matchingintent type in the first Actions widget encountered, it considers the nextancestor Actions widget, and so on, until it reaches the root of the widgettree, or finds a matching intent type and invokes the corresponding action.<topic_end><topic_start>Invoking ActionsThe actions system has several ways to invoke actions.  By far the most commonway is through the use of a Shortcuts widget covered in the previous section,but there are other ways to interrogate the actions subsystem and invoke anaction. It’s possible to invoke actions that are not bound to keys.For instance, to find an action associated with an intent, you can use:<code_start>Action<SelectAllIntent>? selectAll =    Actions.maybeFind<SelectAllIntent>(context);<code_end>This returns an Action associated with the SelectAllIntent type if one isavailable in the given context.  If one isn’t available, it returns null. Ifan associated Action should always be available, then use find instead ofmaybeFind, which throws an exception when it doesn’t find a matching Intenttype.To invoke the action (if it exists), call:<code_start>Object? result;if (selectAll != null) {  result =      Actions.of(context).invokeAction(selectAll, const SelectAllIntent());}<code_end>Combine that into one call with the following:<code_start>Object? result =    Actions.maybeInvoke<SelectAllIntent>(context, const SelectAllIntent());<code_end>Sometimes you want to invoke an action as aresult of pressing a button or another control.You can do this with the Actions.handler function.If the intent has a mapping to an enabled action,the Actions.handler function creates a handler closure.However, if it doesn’t have a mapping, it returns null.This allows the button to be disabled ifthere is no enabled action that matches in the context.<code_start>@overrideWidget build(BuildContext context) {  return Actions(    actions: <Type, Action<Intent>>{      SelectAllIntent: SelectAllAction(model),    },    child: Builder(      builder: (context) => TextButton(        onPressed: Actions.handler<SelectAllIntent>(          context,          SelectAllIntent(controller: controller),        ),        child: const Text('SELECT ALL'),      ),    ),  );}<code_end>The Actions widget only invokes actions when isEnabled(Intent intent)returns true, allowing the action to decide if the dispatcher should consider itfor invocation.  If the action isn’t enabled, then the Actions widget givesanother enabled action higher in the widget hierarchy (if it exists) a chance toexecute.The previous example uses a Builder because Actions.handler andActions.invoke (for example) only finds actions in the provided context, andif the example passes the context given to the build function, the frameworkstarts looking above the current widget.  Using a Builder allows theframework to find the actions defined in the same build function.You can invoke an action without needing a BuildContext, but since theActions widget requires a context to find an enabled action to invoke, youneed to provide one, either by creating your own Action instance, or byfinding one in an appropriate context with Actions.find.To invoke the action, pass the action to the invoke method on anActionDispatcher, either one you created yourself, or one retrieved from anexisting Actions widget using the Actions.of(context) method. Check whetherthe action is enabled before calling invoke. Of course, you can also just callinvoke on the action itself, passing an Intent, but then you opt out of anyservices that an action dispatcher might provide (like logging, undo/redo, andso on).<topic_end><topic_start>Action dispatchersMost of the time, you just want to invoke an action, have it do its thing, andforget about it. Sometimes, however, you might want to log the executed actions.This is where replacing the default ActionDispatcher with a custom dispatchercomes in.  You pass your ActionDispatcher to the Actions widget, and itinvokes actions from any Actions widgets below that one that doesn’t set adispatcher of its own.The first thing Actions does when invoking an action is look up theActionDispatcher and pass the action to it for invocation. If there is none,it creates a default ActionDispatcher that simply invokes the action.If you want a log of all the actions invoked, however, you can create your ownLoggingActionDispatcher to do the job:<code_start>class LoggingActionDispatcher extends ActionDispatcher {  @override  Object? invokeAction(    covariant Action<Intent> action,    covariant Intent intent, [    BuildContext? context,  ]) {    print('Action invoked: $action($intent) from $context');    super.invokeAction(action, intent, context);    return null;  }  @override  (bool, Object?) invokeActionIfEnabled(    covariant Action<Intent> action,    covariant Intent intent, [    BuildContext? context,  ]) {    print('Action invoked: $action($intent) from $context');    return super.invokeActionIfEnabled(action, intent, context);  }}<code_end>Then you pass that to your top-level Actions widget:<code_start>@overrideWidget build(BuildContext context) {  return Actions(    dispatcher: LoggingActionDispatcher(),    actions: <Type, Action<Intent>>{      SelectAllIntent: SelectAllAction(model),    },    child: Builder(      builder: (context) => TextButton(        onPressed: Actions.handler<SelectAllIntent>(          context,          const SelectAllIntent(),        ),        child: const Text('SELECT ALL'),      ),    ),  );}<code_end>This logs every action as it executes, like so:<topic_end><topic_start>Putting it togetherThe combination of Actions and Shortcuts is powerful: you can define genericintents that map to specific actions at the widget level. Here’s a simple appthat illustrates the concepts described above. The app creates a text field thatalso has “select all” and “copy to clipboard” buttons next to it. The buttonsinvoke actions to accomplish their work. All the invoked actions andshortcuts are logged.<code_start>import 'package:flutter/material.dart';import 'package:flutter/services.dart';/// A text field that also has buttons to select all the text and copy the/// selected text to the clipboard.class CopyableTextField extends StatefulWidget {  const CopyableTextField({super.key, required this.title});  final String title;  @override  State<CopyableTextField> createState() => _CopyableTextFieldState();}class _CopyableTextFieldState extends State<CopyableTextField> {  late final TextEditingController controller = TextEditingController();  @override  void dispose() {    controller.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    return Actions(      dispatcher: LoggingActionDispatcher(),      actions: <Type, Action<Intent>>{        ClearIntent: ClearAction(controller),        CopyIntent: CopyAction(controller),        SelectAllIntent: SelectAllAction(controller),      },      child: Builder(builder: (context) {        return Scaffold(          body: Center(            child: Row(              children: <Widget>[                const Spacer(),                Expanded(                  child: TextField(controller: controller),                ),                IconButton(                  icon: const Icon(Icons.copy),                  onPressed:                      Actions.handler<CopyIntent>(context, const CopyIntent()),                ),                IconButton(                  icon: const Icon(Icons.select_all),                  onPressed: Actions.handler<SelectAllIntent>(                      context, const SelectAllIntent()),                ),                const Spacer(),              ],            ),          ),        );      }),    );  }}/// A ShortcutManager that logs all keys that it handles.class LoggingShortcutManager extends ShortcutManager {  @override  KeyEventResult handleKeypress(BuildContext context, KeyEvent event) {    final KeyEventResult result = super.handleKeypress(context, event);    if (result == KeyEventResult.handled) {      print('Handled shortcut $event in $context');    }    return result;  }}/// An ActionDispatcher that logs all the actions that it invokes.class LoggingActionDispatcher extends ActionDispatcher {  @override  Object? invokeAction(    covariant Action<Intent> action,    covariant Intent intent, [    BuildContext? context,  ]) {    print('Action invoked: $action($intent) from $context');    super.invokeAction(action, intent, context);    return null;  }}/// An intent that is bound to ClearAction in order to clear its/// TextEditingController.class ClearIntent extends Intent {  const ClearIntent();}/// An action that is bound to ClearIntent that clears its/// TextEditingController.class ClearAction extends Action<ClearIntent> {  ClearAction(this.controller);  final TextEditingController controller;  @override  Object? invoke(covariant ClearIntent intent) {    controller.clear();    return null;  }}/// An intent that is bound to CopyAction to copy from its/// TextEditingController.class CopyIntent extends Intent {  const CopyIntent();}/// An action that is bound to CopyIntent that copies the text in its/// TextEditingController to the clipboard.class CopyAction extends Action<CopyIntent> {  CopyAction(this.controller);  final TextEditingController controller;  @override  Object? invoke(covariant CopyIntent intent) {    final String selectedString = controller.text.substring(      controller.selection.baseOffset,      controller.selection.extentOffset,    );    Clipboard.setData(ClipboardData(text: selectedString));    return null;  }}/// An intent that is bound to SelectAllAction to select all the text in its/// controller.class SelectAllIntent extends Intent {  const SelectAllIntent();}/// An action that is bound to SelectAllAction that selects all text in its/// TextEditingController.class SelectAllAction extends Action<SelectAllIntent> {  SelectAllAction(this.controller);  final TextEditingController controller;  @override  Object? invoke(covariant SelectAllIntent intent) {    controller.selection = controller.selection.copyWith(      baseOffset: 0,      extentOffset: controller.text.length,      affinity: controller.selection.affinity,    );    return null;  }}/// The top level application class.////// Shortcuts defined here are in effect for the whole app,/// although different widgets may fulfill them differently.class MyApp extends StatelessWidget {  const MyApp({super.key});  static const String title = 'Shortcuts and Actions Demo';  @override  Widget build(BuildContext context) {    return MaterialApp(      title: title,      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: Shortcuts(        shortcuts: <LogicalKeySet, Intent>{          LogicalKeySet(LogicalKeyboardKey.escape): const ClearIntent(),          LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyC):              const CopyIntent(),          LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyA):              const SelectAllIntent(),        },        child: const CopyableTextField(title: title),      ),    );  }}void main() => runApp(const MyApp());<code_end><topic_end><topic_start>Understanding Flutter's keyboard focus systemThis article explains how to control where keyboard input is directed. If youare implementing an application that uses a physical keyboard, such as mostdesktop and web applications, this page is for you. If your app won’t be usedwith a physical keyboard, you can skip this.<topic_end><topic_start>OverviewFlutter comes with a focus system that directs the keyboard input to aparticular part of an application. In order to do this, users “focus” the inputonto that part of an application by tapping or clicking the desired UI element.Once that happens, text entered with the keyboard flows to that part of theapplication until the focus moves to another part of the application.  Focus canalso be moved by pressing a particular keyboard shortcut, which is typicallybound to Tab, so it is sometimes called “tab traversal”.This page explores the APIs used to perform these operations on a Flutterapplication, and how the focus system works. We have noticed that there is someconfusion among developers about how to define and use FocusNode objects.If that describes your experience, skip ahead to the best practices forcreating FocusNode objects.<topic_end><topic_start>Focus use casesSome examples of situations where you might need to know how to use the focussystem:<topic_end><topic_start>GlossaryBelow are terms, as Flutter uses them, for elements of the focus system. Thevarious classes that implement some of these concepts are introduced below.<topic_end><topic_start>FocusNode and FocusScopeNodeThe FocusNode and FocusScopeNode objects implement themechanics of the focus system. They are long-lived objects (longer than widgets,similar to render objects) that hold the focus state and attributes so that theyare persistent between builds of the widget tree. Together, they formthe focus tree data structure.They were originally intended to be developer-facing objects used to controlsome aspects of the focus system, but over time they have evolved to mostlyimplement details of the focus system. In order to prevent breaking existingapplications, they still contain public interfaces for their attributes. But, ingeneral, the thing for which they are most useful is to act as a relativelyopaque handle, passed to a descendant widget in order to call requestFocus()on an ancestor widget, which requests that a descendant widget obtain focus.Setting of the other attributes is best managed by a Focus orFocusScope widget, unless you are not using them, or implementing your ownversion of them.<topic_end><topic_start>Best practices for creating FocusNode objectsSome dos and don’ts around using these objects include:<topic_end><topic_start>UnfocusingThere is an API for telling a node to “give up the focus”, namedFocusNode.unfocus(). While it does remove focus from the node, it is importantto realize that there really is no such thing as “unfocusing” all nodes. If anode is unfocused, then it must pass the focus somewhere else, since there isalways a primary focus. The node that receives the focus when a node callsunfocus() is either the nearest FocusScopeNode, or a previously focused nodein that scope, depending upon the disposition argument given to unfocus().If you would like more control over where the focus goes when you remove it froma node, explicitly focus another node instead of calling unfocus(), or use thefocus traversal mechanism to find another node with the focusInDirection,nextFocus, or previousFocus methods on FocusNode.When calling unfocus(), the disposition argument allows two modes forunfocusing: UnfocusDisposition.scope andUnfocusDisposition.previouslyFocusedChild. The default is scope, which givesthe focus to the nearest parent focus scope. This means that if the focus isthereafter moved to the next node with FocusNode.nextFocus, it starts with the“first” focusable item in the scope.The previouslyFocusedChild disposition will search the scope to find thepreviously focused child and request focus on it. If there is no previouslyfocused child, it is equivalent to scope.Beware: If there is no other scope, then focus moves to the root scope node of  the focus system, FocusManager.rootScope. This is generally not desirable, as  the root scope doesn’t have a context for the framework to determine which  node should be focused next. If you find that your application suddenly loses  the ability to navigate by using focus traversal, this is probably what has  happened.  To fix it, add a FocusScope as an ancestor to the focus node that  is requesting the unfocus. The WidgetsApp (from which MaterialApp and  CupertinoApp are derived) has its own FocusScope, so this should not be an  issue if you are using those.<topic_end><topic_start>Focus widgetThe Focus widget owns and manages a focus node, and is the workhorse of thefocus system.  It manages the attaching and detaching of the focus node it ownsfrom the focus tree, manages the attributes and callbacks of the focus node, andhas static functions to enable discovery of focus nodes attached to the widgettree.In its simplest form, wrapping the Focus widget around a widget subtree allowsthat widget subtree to obtain focus as part of the focus traversal process, orwhenever requestFocus is called on the FocusNode passed to it. When combinedwith a gesture detector that calls requestFocus, it can receive focus whentapped or clicked.You might pass a FocusNode object to the Focus widget to manage, but if youdon’t, it creates its own. The main reason to create your ownFocusNode is to be able to call requestFocus()on the node to control the focus from a parent widget. Most of the otherfunctionality of a FocusNode is best accessed by changing the attributes ofthe Focus widget itself.The Focus widget is used in most of Flutter’s own controls to implement theirfocus functionality.Here is an example showing how to use the Focus widget to make a customcontrol focusable. It creates a container with text that reacts to receiving thefocus.<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  static const String _title = 'Focus Sample';  @override  Widget build(BuildContext context) {    return MaterialApp(      title: _title,      home: Scaffold(        appBar: AppBar(title: const Text(_title)),        body: const Column(          mainAxisAlignment: MainAxisAlignment.center,          children: <Widget>[MyCustomWidget(), MyCustomWidget()],        ),      ),    );  }}class MyCustomWidget extends StatefulWidget {  const MyCustomWidget({super.key});  @override  State<MyCustomWidget> createState() => _MyCustomWidgetState();}class _MyCustomWidgetState extends State<MyCustomWidget> {  Color _color = Colors.white;  String _label = 'Unfocused';  @override  Widget build(BuildContext context) {    return Focus(      onFocusChange: (focused) {        setState(() {          _color = focused ? Colors.black26 : Colors.white;          _label = focused ? 'Focused' : 'Unfocused';        });      },      child: Center(        child: Container(          width: 300,          height: 50,          alignment: Alignment.center,          color: _color,          child: Text(_label),        ),      ),    );  }}<code_end><topic_end><topic_start>Key eventsIf you wish to listen for key events in a subtree,set the onKeyEvent attribute of the Focus widget tobe a handler that either just listens to the key, orhandles the key and stops its propagation to other widgets.Key events start at the focus node with primary focus.If that node doesn’t return KeyEventResult.handled fromits onKeyEvent handler, then its parent focus node is given the event.If the parent doesn’t handle it, it goes to its parent,and so on, until it reaches the root of the focus tree.If the event reaches the root of the focus tree without being handled, thenit is returned to the platform to give tothe next native control in the application(in case the Flutter UI is part of a larger native application UI).Events that are handled are not propagated to other Flutter widgets,and they are also not propagated to native widgets.Here’s an example of a Focus widget that absorbs every key thatits subtree doesn’t handle, without being able to be the primary focus:<code_start>@overrideWidget build(BuildContext context) {  return Focus(    onKeyEvent: (node, event) => KeyEventResult.handled,    canRequestFocus: false,    child: child,  );}<code_end>Focus key events are processed before text entry events, so handling a key eventwhen the focus widget surrounds a text field prevents that key from beingentered into the text field.Here’s an example of a widget that won’t allow the letter “a” to be typed intothe text field:<code_start>@overrideWidget build(BuildContext context) {  return Focus(    onKeyEvent: (node, event) {      return (event.logicalKey == LogicalKeyboardKey.keyA)          ? KeyEventResult.handled          : KeyEventResult.ignored;    },    child: const TextField(),  );}<code_end>If the intent is input validation, this example’s functionality would probablybe better implemented using a TextInputFormatter, but the technique can stillbe useful: the Shortcuts widget uses this method to handle shortcuts beforethey become text input, for instance.<topic_end><topic_start>Controlling what gets focusOne of the main aspects of focus is controlling what can receive focus and how.The attributes canRequestFocus, skipTraversal, and descendantsAreFocusablecontrol how this node and its descendants participate in the focus process.If the skipTraversal attribute true, then this focus node doesn’t participatein focus traversal. It is still focusable if requestFocus is called on itsfocus node, but is otherwise skipped when the focus traversal system is lookingfor the next thing to focus on.The canRequestFocus attribute, unsurprisingly, controls whether or not thefocus node that this Focus widget manages can be used to request focus. Ifthis attribute is false, then calling requestFocus on the node has no effect.It also implies that this node is skipped for focus traversal, since it can’trequest focus.The descendantsAreFocusable attribute controls whether the descendants of thisnode can receive focus, but still allows this node to receive focus.  Thisattribute can be used to turn off focusability for an entire widget subtree.This is how the ExcludeFocus widget works: it’s just a Focus widget withthis attribute set.<topic_end><topic_start>AutofocusSetting the autofocus attribute of a Focus widget tells the widget torequest the focus the first time the focus scope it belongs to is focused.  Ifmore than one widget has autofocus set, then it is arbitrary which onereceives the focus, so try to only set it on one widget per focus scope.The autofocus attribute only takes effect if there isn’t already a focus inthe scope that the node belongs to.Setting the autofocus attribute on two nodes that belong to different focusscopes is well defined: each one becomes the focused widget when theircorresponding scopes are focused.<topic_end><topic_start>Change notificationsThe Focus.onFocusChanged callback can be used to get notifications that thefocus state for a particular node has changed. It notifies if the node is addedto or removed from the focus chain, which means it gets notifications even if itisn’t the primary focus. If you only want to know if you have received theprimary focus, check and see if hasPrimaryFocus is true on the focus node.<topic_end><topic_start>Obtaining the FocusNodeSometimes, it is useful to obtain the focus node of a Focus widget tointerrogate its attributes.To access the focus node from an ancestor of the Focus widget, create and passin a FocusNode as the Focus widget’s focusNode attribute. Because it needsto be disposed of, the focus node you pass needs to be owned by a statefulwidget, so don’t just create one each time it is built.If you need access to the focus node from the descendant of a Focus widget,you can call Focus.of(context) to obtain the focus node of the nearest Focuswidget to the given context. If you need to obtain the FocusNode of a Focuswidget within the same build function, use a Builder to make sure you havethe correct context. This is shown in the following example:<code_start>@overrideWidget build(BuildContext context) {  return Focus(    child: Builder(      builder: (context) {        final bool hasPrimary = Focus.of(context).hasPrimaryFocus;        print('Building with primary focus: $hasPrimary');        return const SizedBox(width: 100, height: 100);      },    ),  );}<code_end><topic_end><topic_start>TimingOne of the details of the focus system is that when focus is requested, it onlytakes effect after the current build phase completes.  This means that focuschanges are always delayed by one frame, because changing focus cancause arbitrary parts of the widget tree to rebuild, including ancestors of thewidget currently requesting focus. Because descendants cannot dirty theirancestors, it has to happen between frames, so that any needed changes canhappen on the next frame.<topic_end><topic_start>FocusScope widgetThe FocusScope widget is a special version of the Focus widget that managesa FocusScopeNode instead of a FocusNode.  The FocusScopeNode is a specialnode in the focus tree that serves as a grouping mechanism for the focus nodesin a subtree. Focus traversal stays within a focus scope unless a node outsideof the scope is explicitly focused.The focus scope also keeps track of the current focus and history of the nodesfocused within its subtree.  That way, if a node releases focus or is removedwhen it had focus, the focus can be returned to the node that had focuspreviously.Focus scopes also serve as a place to return focus to if none of the descendantshave focus.  This allows the focus traversal code to have a starting context forfinding the next (or first) focusable control to move to.If you focus a focus scope node, it first attempts to focus the current, or mostrecently focused node in its subtree, or the node in its subtree that requestedautofocus (if any).  If there is no such node, it receives the focus itself.<topic_end><topic_start>FocusableActionDetector widgetThe FocusableActionDetector is a widget that combines the functionality ofActions, Shortcuts, MouseRegion and a Focus widget to createa detector that defines actions and key bindings, and provides callbacks forhandling focus and hover highlights. It is what Flutter controls use toimplement all of these aspects of the controls. It is just implemented using theconstituent widgets, so if you don’t need all of its functionality, you can justuse the ones you need, but it is a convenient way to build these behaviors intoyour custom controls.info Note  To learn more, watch this short Widget of the Week video on the FocusableActionDetector widget:<topic_end><topic_start>Controlling focus traversalOnce an application has the ability to focus, the next thing many apps want todo is to allow the user to control the focus using the keyboard or another inputdevice. The most common example of this is “tab traversal” where the userpresses Tab to go to the “next” control. Controlling what “next”means is the subject of this section. This kind of traversal is provided byFlutter by default.In a simple grid layout, it’s fairly easy to decide which control is next. Ifyou’re not at the end of the row, then it’s the one to the right (or left forright-to-left locales). If you are at the end of a row, it’s the first controlin the next row. Unfortunately, applications are rarely laid out in grids, somore guidance is often needed.The default algorithm in Flutter (ReadingOrderTraversalPolicy) for focustraversal is pretty good: It gives the right answer for most applications.However, there are always pathological cases, or cases where the context ordesign requires a different order than the one the default ordering algorithmarrives at. For those cases, there are other mechanisms for achieving thedesired order.<topic_end><topic_start>FocusTraversalGroup widgetThe FocusTraversalGroup widget should be placed in the tree around widgetsubtrees that should be fully traversed before moving on to another widget orgroup of widgets. Just grouping widgets into related groups is often enough toresolve many tab traversal ordering problems. If not, the group can also begiven a FocusTraversalPolicy to determine the ordering within the group.The default ReadingOrderTraversalPolicy is usually sufficient, but incases where more control over ordering is needed, anOrderedTraversalPolicy can be used. The order argument of theFocusTraversalOrder widget wrapped around the focusable componentsdetermines the order. The order can be any subclass of FocusOrder, butNumericFocusOrder and LexicalFocusOrder are provided.If none of the provided focus traversal policies are sufficient for yourapplication, you could also write your own policy and use it to determine anycustom ordering you want.Here’s an example of how to use the FocusTraversalOrder widget to traverse arow of buttons in the order TWO, ONE, THREE using NumericFocusOrder.<code_start>class OrderedButtonRow extends StatelessWidget {  const OrderedButtonRow({super.key});  @override  Widget build(BuildContext context) {    return FocusTraversalGroup(      policy: OrderedTraversalPolicy(),      child: Row(        children: <Widget>[          const Spacer(),          FocusTraversalOrder(            order: const NumericFocusOrder(2),            child: TextButton(              child: const Text('ONE'),              onPressed: () {},            ),          ),          const Spacer(),          FocusTraversalOrder(            order: const NumericFocusOrder(1),            child: TextButton(              child: const Text('TWO'),              onPressed: () {},            ),          ),          const Spacer(),          FocusTraversalOrder(            order: const NumericFocusOrder(3),            child: TextButton(              child: const Text('THREE'),              onPressed: () {},            ),          ),          const Spacer(),        ],      ),    );  }}<code_end><topic_end><topic_start>FocusTraversalPolicyThe FocusTraversalPolicy is the object that determines which widget is next,given a request and the current focus node. The requests (member functions) arethings like findFirstFocus, findLastFocus, next, previous, andinDirection.FocusTraversalPolicy is the abstract base class for concrete policies, likeReadingOrderTraversalPolicy,  OrderedTraversalPolicy and theDirectionalFocusTraversalPolicyMixin classes.In order to use a FocusTraversalPolicy, you give one to aFocusTraversalGroup, which determines the widget subtree in which the policywill be effective. The member functions of the class are rarely called directly:they are meant to be used by the focus system.<topic_end><topic_start>The focus managerThe FocusManager maintains the current primary focus for the system. Itonly has a few pieces of API that are useful to users of the focus system. Oneis the FocusManager.instance.primaryFocus property, which contains thecurrently focused focus node and is also accessible from the globalprimaryFocus field.Other useful properties are FocusManager.instance.highlightMode andFocusManager.instance.highlightStrategy. These are used by widgets that needto switch between a “touch” mode and a “traditional” (mouse and keyboard) modefor their focus highlights. When a user is using touch to navigate, the focushighlight is usually hidden, and when they switch to a mouse or keyboard, thefocus highlight needs to be shown again so they know what is focused. ThehightlightStrategy tells the focus manager how to interpret changes in theusage mode of the device: it can either automatically switch between the twobased on the most recent input events, or it can be locked in touch ortraditional modes. The provided widgets in Flutter already know how to use thisinformation, so you only need it if you’re writing your own controls fromscratch. You can use addHighlightModeListener callback to listen for changesin the highlight mode.<topic_end><topic_start>Adding assets and imagesFlutter apps can include both code and assets(sometimes called resources). An asset is a filethat is bundled and deployed with your app,and is accessible at runtime. Common types of assets includestatic data (for example, JSON files),configuration files, icons, and images(JPEG, WebP, GIF, animated WebP/GIF, PNG, BMP, and WBMP).<topic_end><topic_start>Specifying assetsFlutter uses the pubspec.yaml file,located at the root of your project,to identify assets required by an app.Here is an example:To include all assets under a directory,specify the directory name with the / character at the end:info Note Only files located directly in the directory are included. Resolution-aware asset image variants are the only exception. To add files located in subdirectories, create an entry per directory.<topic_end><topic_start>Asset bundlingThe assets subsection of the flutter sectionspecifies files that should be included with the app.Each asset is identified by an explicit path(relative to the pubspec.yaml file) where the assetfile is located. The order in which the assets aredeclared doesn’t matter. The actual directory name used(assets in first example or directory in the aboveexample) doesn’t matter.During a build, Flutter places assets into a specialarchive called the asset bundle that apps readfrom at runtime.<topic_end><topic_start>Loading assetsYour app can access its assets through anAssetBundle object.The two main methods on an asset bundle allow you to load astring/text asset (loadString()) or an image/binary asset (load())out of the bundle, given a logical key. The logical key maps to the pathto the asset specified in the pubspec.yaml file at build time.<topic_end><topic_start>Loading text assetsEach Flutter app has a rootBundleobject for easy access to the main asset bundle.It is possible to load assets directly using therootBundle global static frompackage:flutter/services.dart.However, it’s recommended to obtain the AssetBundlefor the current BuildContext usingDefaultAssetBundle, rather than the defaultasset bundle that was built with the app; thisapproach enables a parent widget to substitute adifferent AssetBundle at run time,which can be useful for localization or testingscenarios.Typically, you’ll use DefaultAssetBundle.of()to indirectly load an asset, for example a JSON file,from the app’s runtime rootBundle.Outside of a Widget context, or when a handleto an AssetBundle is not available,you can use rootBundle to directly load such assets.For example:<code_start>import 'package:flutter/services.dart' show rootBundle;Future<String> loadAsset() async {  return await rootBundle.loadString('assets/config.json');}<code_end><topic_end><topic_start>Loading imagesTo load an image, use the AssetImageclass in a widget’s build() method.For example, your app can load the backgroundimage from the asset declarations in the previous example:<code_start>return const Image(image: AssetImage('assets/background.png'));<code_end><topic_end><topic_start>Resolution-aware image assetsFlutter can load resolution-appropriate images forthe current device pixel ratio.AssetImage will map a logical requestedasset onto one that most closely matches the currentdevice pixel ratio.For this mapping to work, assets should be arrangedaccording to a particular directory structure:Where M and N are numeric identifiers that correspondto the nominal resolution of the images contained within.In other words, they specify the device pixel ratio thatthe images are intended for.In this example, image.png is considered the main asset,while Mx/image.png and Nx/image.png are considered to bevariants.The main asset is assumed to correspond to a resolution of 1.0.For example, consider the following asset layout for animage named my_icon.png:On devices with a device pixel ratio of 1.8, the asset.../2.0x/my_icon.png is chosen.For a device pixel ratio of 2.7, the asset.../3.0x/my_icon.png is chosen.If the width and height of the rendered image are not specifiedon the Image widget, the nominal resolution is used to scalethe asset so that it occupies the same amount of screen spaceas the main asset would have, just with a higher resolution.That is, if .../my_icon.png is 72px by 72px, then.../3.0x/my_icon.png should be 216px by 216px;but they both render into 72px by 72px (in logical pixels),if width and height are not specified.info NoteDevice pixel ratio depends on MediaQueryData.size, which requires having either  MaterialApp or CupertinoApp as an ancestor of your AssetImage.<topic_end><topic_start>Bundling of resolution-aware image assetsYou only need to specify the main asset or its parent directoryin the assets section of pubspec.yaml.Flutter bundles the variants for you.Each entry should correspond to a real file, with the exception ofthe main asset entry. If the main asset entry doesn’t correspondto a real file, then the asset with the lowest resolutionis used as the fallback for devices with device pixelratios below that resolution. The entry should stillbe included in the pubspec.yaml manifest, however.Anything using the default asset bundle inherits resolutionawareness when loading images. (If you work with some of the lowerlevel classes, like ImageStream or ImageCache,you’ll also notice parameters related to scale.)<topic_end><topic_start>Asset images in package dependenciesTo load an image from a package dependency,the package argument must be provided to AssetImage.For instance, suppose your application depends on a packagecalled my_icons, which has the following directory structure:To load the image, use:<code_start>return const AssetImage('icons/heart.png', package: 'my_icons');<code_end>Assets used by the package itself should also be fetchedusing the package argument as above.<topic_end><topic_start>Bundling of package assetsIf the desired asset is specified in the pubspec.yamlfile of the package, it’s bundled automatically with theapplication. In particular, assets used by the packageitself must be specified in its pubspec.yaml.A package can also choose to have assets in its lib/folder that are not specified in its pubspec.yaml file.In this case, for those images to be bundled,the application has to specify which ones to include in itspubspec.yaml. For instance, a package named fancy_backgroundscould have the following files:To include, say, the first image, the pubspec.yaml of theapplication should specify it in the assets section:The lib/ is implied,so it should not be included in the asset path.If you are developing a package, to load an asset within the package, specify it in the pubspec.yaml of the package:To load the image within your package, use:<topic_end><topic_start>Sharing assets with the underlying platformFlutter assets are readily available to platform codeusing the AssetManager on Android and NSBundle on iOS.<topic_end><topic_start>Loading Flutter assets in AndroidOn Android the assets are available through theAssetManager API.  The lookup key used in,for instance openFd, is obtained fromlookupKeyForAsset on PluginRegistry.Registrar orgetLookupKeyForAsset on FlutterView.PluginRegistry.Registrar is available when developing a pluginwhile FlutterView would be the choice when developing anapp including a platform view.As an example, suppose you have specified the followingin your pubspec.yamlThis reflects the following structure in your Flutter app.To access icons/heart.png from your Java plugin code,do the following:<topic_end><topic_start>Loading Flutter assets in iOSOn iOS the assets are available through the mainBundle.The lookup key used in, for instance pathForResource:ofType:,is obtained from lookupKeyForAsset or lookupKeyForAsset:fromPackage:on FlutterPluginRegistrar, or lookupKeyForAsset: orlookupKeyForAsset:fromPackage: on FlutterViewController.FlutterPluginRegistrar is available when developinga plugin while FlutterViewController would be the choicewhen developing an app including a platform view.As an example, suppose you have the Flutter setting from above.To access icons/heart.png from your Objective-C plugin code youwould do the following:To access icons/heart.png from your Swift app youwould do the following:For a more complete example, see the implementation of theFlutter video_player plugin on pub.dev.The ios_platform_images plugin on pub.dev wrapsup this logic in a convenient category. You fetchan image as follows:Objective-C:Swift:<topic_end><topic_start>Loading iOS images in FlutterWhen implementing Flutter byadding it to an existing iOS app,you might have images hosted in iOS that youwant to use in Flutter. To accomplishthat, use the ios_platform_images pluginavailable on pub.dev.<topic_end><topic_start>Platform assetsThere are other occasions to work with assets in theplatform projects directly. Below are two common caseswhere assets are used before the Flutter framework isloaded and running.<topic_end><topic_start>Updating the app iconUpdating a Flutter application’s launch icon worksthe same way as updating launch icons in nativeAndroid or iOS applications.<topic_end><topic_start>AndroidIn your Flutter project’s root directory, navigate to.../android/app/src/main/res. The various bitmap resourcefolders such as mipmap-hdpi already contain placeholderimages named ic_launcher.png. Replace them with yourdesired assets respecting the recommended icon size perscreen density as indicated by the Android Developer Guide.info Note  If you rename the .png files, you must also update the  corresponding name in your AndroidManifest.xml’s  <application> tag’s android:icon attribute.<topic_end><topic_start>iOSIn your Flutter project’s root directory,navigate to .../ios/Runner. TheAssets.xcassets/AppIcon.appiconset directory already containsplaceholder images. Replace them with the appropriatelysized images as indicated by their filename as dictated by theApple Human Interface Guidelines.Keep the original file names.<topic_end><topic_start>Updating the launch screenFlutter also uses native platform mechanisms to drawtransitional launch screens to your Flutter app while theFlutter framework loads. This launch screen persists untilFlutter renders the first frame of your application.info Note  This implies that if you don’t call runApp() in the  main() function of your app (or more specifically,  if you don’t call FlutterView.render() in response to  PlatformDispatcher.onDrawFrame),  the launch screen persists forever.<topic_end><topic_start>AndroidTo add a launch screen (also known as “splash screen”) to yourFlutter application, navigate to .../android/app/src/main.In res/drawable/launch_background.xml,use this layer list drawable XML to customizethe look of your launch screen. The existing template providesan example of adding an image to the middle of a white splashscreen in commented code. You can uncomment it or use otherdrawables to achieve the intended effect.For more details, seeAdding a splash screen to your Android app.<topic_end><topic_start>iOSTo add an image to the center of your “splash screen”,navigate to .../ios/Runner.In Assets.xcassets/LaunchImage.imageset,drop in images named LaunchImage.png,LaunchImage@2x.png, LaunchImage@3x.png.If you use different filenames,update the Contents.json file in the same directory.You can also fully customize your launch screen storyboardin Xcode by opening .../ios/Runner.xcworkspace.Navigate to Runner/Runner in the Project Navigator anddrop in images by opening Assets.xcassets or do anycustomization using the Interface Builder inLaunchScreen.storyboard.For more details, seeAdding a splash screen to your iOS app.<topic_end><topic_start>Display images from the internetDisplaying images is fundamental for most mobile apps.Flutter provides the Image widget todisplay different types of images.To work with images from a URL, use theImage.network() constructor.<code_start>Image.network('https://picsum.photos/250?image=9'),<code_end><topic_end><topic_start>Bonus: animated gifsOne useful thing about the Image widget:It supports animated gifs.<code_start>Image.network(    'https://docs.flutter.dev/assets/images/dash/dash-fainting.gif');<code_end><topic_end><topic_start>Image fade in with placeholdersThe default Image.network constructor doesn’t handle more advancedfunctionality, such as fading images in after loading.To accomplish this task,check out Fade in images with a placeholder.<topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    var title = 'Web Images';    return MaterialApp(      title: title,      home: Scaffold(        appBar: AppBar(          title: Text(title),        ),        body: Image.network('https://picsum.photos/250?image=9'),      ),    );  }}<code_end><topic_end><topic_start>Fade in images with a placeholderWhen displaying images using the default Image widget,you might notice they simply pop onto the screen as they’re loaded.This might feel visually jarring to your users.Instead, wouldn’t it be nice to display a placeholder at first,and images would fade in as they’re loaded? Use theFadeInImage widget for exactly this purpose.FadeInImage works with images of any type: in-memory, local assets,or images from the internet.<topic_end><topic_start>In-MemoryIn this example, use the transparent_imagepackage for a simple transparent placeholder.<code_start>FadeInImage.memoryNetwork(  placeholder: kTransparentImage,  image: 'https://picsum.photos/250?image=9',),<code_end><topic_end><topic_start>Complete example<code_start>import 'package:flutter/material.dart';import 'package:transparent_image/transparent_image.dart';void main() {  runApp(const MyApp());}class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const title = 'Fade in images';    return MaterialApp(      title: title,      home: Scaffold(        appBar: AppBar(          title: const Text(title),        ),        body: Stack(          children: <Widget>[            const Center(child: CircularProgressIndicator()),            Center(              child: FadeInImage.memoryNetwork(                placeholder: kTransparentImage,                image: 'https://picsum.photos/250?image=9',              ),            ),          ],        ),      ),    );  }}<code_end><topic_end><topic_start>From asset bundleYou can also consider using local assets for placeholders.First, add the asset to the project’s pubspec.yaml file(for more details, see Adding assets and images):Then, use the FadeInImage.assetNetwork() constructor:<code_start>FadeInImage.assetNetwork(  placeholder: 'assets/loading.gif',  image: 'https://picsum.photos/250?image=9',),<code_end><topic_end><topic_start>Complete example<code_start>import 'package:flutter/material.dart';void main() {  runApp(const MyApp());}class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const title = 'Fade in images';    return MaterialApp(      title: title,      home: Scaffold(        appBar: AppBar(          title: const Text(title),        ),        body: Center(          child: FadeInImage.assetNetwork(            placeholder: 'assets/loading.gif',            image: 'https://picsum.photos/250?image=9',          ),        ),      ),    );  }}<code_end><topic_end><topic_start>Play and pause a videoPlaying videos is a common task in app development,and Flutter apps are no exception. To play videos,the Flutter team provides the video_player plugin.You can use the video_player plugin to play videosstored on the file system, as an asset, or from the internet.warning Warning  At this time,  the video_player plugin doesn’t work with any desktop platform.  To learn more, check out the video_player package.On iOS, the video_player plugin makes use ofAVPlayer to handle playback. On Android,it uses ExoPlayer.This recipe demonstrates how to use the video_player package to stream avideo from the internet with basic play and pause controls usingthe following steps:<topic_end><topic_start>1. Add the video_player dependencyThis recipe depends on one Flutter plugin: video_player. First, add this dependency to your project.To add the video_player package as a dependency, run flutter pub add:<topic_end><topic_start>2. Add permissions to your appNext, update your android and ios configurations to ensurethat your app has the correct permissions to stream videosfrom the internet.<topic_end><topic_start>AndroidAdd the following permission to the AndroidManifest.xml file just after the<application> definition. The AndroidManifest.xml file is found at<project root>/android/app/src/main/AndroidManifest.xml.<topic_end><topic_start>iOSFor iOS, add the following to the Info.plist file found at<project root>/ios/Runner/Info.plist.warning Warning  The video_player plugin can only play asset videos in iOS simulators.  You must test network-hosted videos on physical iOS devices.<topic_end><topic_start>3. Create and initialize a VideoPlayerControllerNow that you have the video_player plugin installed with the correctpermissions, create a VideoPlayerController. TheVideoPlayerController class allows you to connect to different types ofvideos and control playback.Before you can play videos, you must also initialize the controller.This establishes the connection to the video and prepare thecontroller for playback.To create and initialize the VideoPlayerController do the following:<code_start>class VideoPlayerScreen extends StatefulWidget {  const VideoPlayerScreen({super.key});  @override  State<VideoPlayerScreen> createState() => _VideoPlayerScreenState();}class _VideoPlayerScreenState extends State<VideoPlayerScreen> {  late VideoPlayerController _controller;  late Future<void> _initializeVideoPlayerFuture;  @override  void initState() {    super.initState();    // Create and store the VideoPlayerController. The VideoPlayerController    // offers several different constructors to play videos from assets, files,    // or the internet.    _controller = VideoPlayerController.networkUrl(      Uri.parse(        'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',      ),    );    _initializeVideoPlayerFuture = _controller.initialize();  }  @override  void dispose() {    // Ensure disposing of the VideoPlayerController to free up resources.    _controller.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    // Complete the code in the next step.    return Container();  }}<code_end><topic_end><topic_start>4. Display the video playerNow, display the video. The video_player plugin provides theVideoPlayer widget to display the video initialized bythe VideoPlayerController.By default, the VideoPlayer widget takes up as much space as possible.This often isn’t ideal for videos because they are meantto be displayed in a specific aspect ratio, such as 16x9 or 4x3.Therefore, wrap the VideoPlayer widget in an AspectRatiowidget to ensure that the video has the correct proportions.Furthermore, you must display the VideoPlayer widget after the_initializeVideoPlayerFuture() completes. Use FutureBuilder todisplay a loading spinner until the controller finishes initializing.Note: initializing the controller does not begin playback.<code_start>// Use a FutureBuilder to display a loading spinner while waiting for the// VideoPlayerController to finish initializing.FutureBuilder(  future: _initializeVideoPlayerFuture,  builder: (context, snapshot) {    if (snapshot.connectionState == ConnectionState.done) {      // If the VideoPlayerController has finished initialization, use      // the data it provides to limit the aspect ratio of the video.      return AspectRatio(        aspectRatio: _controller.value.aspectRatio,        // Use the VideoPlayer widget to display the video.        child: VideoPlayer(_controller),      );    } else {      // If the VideoPlayerController is still initializing, show a      // loading spinner.      return const Center(        child: CircularProgressIndicator(),      );    }  },)<code_end><topic_end><topic_start>5. Play and pause the videoBy default, the video starts in a paused state. To begin playback,call the play() method provided by the VideoPlayerController.To pause playback, call the pause() method.For this example,add a FloatingActionButton to your app that displays a playor pause icon depending on the situation.When the user taps the button,play the video if it’s currently paused,or pause the video if it’s playing.<code_start>FloatingActionButton(  onPressed: () {    // Wrap the play or pause in a call to `setState`. This ensures the    // correct icon is shown.    setState(() {      // If the video is playing, pause it.      if (_controller.value.isPlaying) {        _controller.pause();      } else {        // If the video is paused, play it.        _controller.play();      }    });  },  // Display the correct icon depending on the state of the player.  child: Icon(    _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,  ),)<code_end><topic_end><topic_start>Complete example<code_start>import 'dart:async';import 'package:flutter/material.dart';import 'package:video_player/video_player.dart';void main() => runApp(const VideoPlayerApp());class VideoPlayerApp extends StatelessWidget {  const VideoPlayerApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Video Player Demo',      home: VideoPlayerScreen(),    );  }}class VideoPlayerScreen extends StatefulWidget {  const VideoPlayerScreen({super.key});  @override  State<VideoPlayerScreen> createState() => _VideoPlayerScreenState();}class _VideoPlayerScreenState extends State<VideoPlayerScreen> {  late VideoPlayerController _controller;  late Future<void> _initializeVideoPlayerFuture;  @override  void initState() {    super.initState();    // Create and store the VideoPlayerController. The VideoPlayerController    // offers several different constructors to play videos from assets, files,    // or the internet.    _controller = VideoPlayerController.networkUrl(      Uri.parse(        'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4',      ),    );    // Initialize the controller and store the Future for later use.    _initializeVideoPlayerFuture = _controller.initialize();    // Use the controller to loop the video.    _controller.setLooping(true);  }  @override  void dispose() {    // Ensure disposing of the VideoPlayerController to free up resources.    _controller.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Butterfly Video'),      ),      // Use a FutureBuilder to display a loading spinner while waiting for the      // VideoPlayerController to finish initializing.      body: FutureBuilder(        future: _initializeVideoPlayerFuture,        builder: (context, snapshot) {          if (snapshot.connectionState == ConnectionState.done) {            // If the VideoPlayerController has finished initialization, use            // the data it provides to limit the aspect ratio of the video.            return AspectRatio(              aspectRatio: _controller.value.aspectRatio,              // Use the VideoPlayer widget to display the video.              child: VideoPlayer(_controller),            );          } else {            // If the VideoPlayerController is still initializing, show a            // loading spinner.            return const Center(              child: CircularProgressIndicator(),            );          }        },      ),      floatingActionButton: FloatingActionButton(        onPressed: () {          // Wrap the play or pause in a call to `setState`. This ensures the          // correct icon is shown.          setState(() {            // If the video is playing, pause it.            if (_controller.value.isPlaying) {              _controller.pause();            } else {              // If the video is paused, play it.              _controller.play();            }          });        },        // Display the correct icon depending on the state of the player.        child: Icon(          _controller.value.isPlaying ? Icons.pause : Icons.play_arrow,        ),      ),    );  }}<code_end><topic_end><topic_start>Navigation and routingFlutter provides a complete system for navigating between screens and handlingdeep links. Small applications without complex deep linking can useNavigator, while apps with specific deep linking and navigationrequirements should also use the Router to correctly handle deep links onAndroid and iOS, and to stay in sync with the address bar when the app isrunning on the web.To configure your Android or iOS application to handle deep links, see Deep linking.<topic_end><topic_start>Using the NavigatorThe Navigator widget displays screens as a stack using the correct transitionanimations for the target platform. To navigate to a new screen, access theNavigator through the route’s BuildContext and call imperative methods suchas push() or pop():Because Navigator keeps a stack of Route objects (representing the historystack), The push() method also takes a Route object. The MaterialPageRouteobject is a subclass of Route that specifies the transition animations forMaterial Design. For more examples of how to use the Navigator, follow thenavigation recipes from the Flutter Cookbook or visit the Navigator APIdocumentation.<topic_end><topic_start>Using named routesinfo Note  We don’t recommend using named routes for most applications.  For more information, see the Limitations section below.Applications with simple navigation and deep linking requirements can use theNavigator for navigation and the MaterialApp.routes parameter for deeplinks:Routes specified here are called named routes. For a complete example, followthe Navigate with named routes recipe from the Flutter Cookbook.<topic_end><topic_start>LimitationsAlthough named routes can handle deep links, the behavior is always the same andcan’t be customized. When a new deep link is received by the platform, Flutterpushes a new Route onto the Navigator regardless where the user currently is.Flutter also doesn’t support the browser forward button for applications usingnamed routes. For these reasons, we don’t recommend using named routes in mostapplications.<topic_end><topic_start>Using the RouterFlutter applications with advanced navigation and routing requirements (such asa web app that uses direct links to each screen, or an app with multipleNavigator widgets) should use a routing package such as go_router that canparse the route path and configure the Navigator whenever the app receives anew deep link.To use the Router, switch to the router constructor on MaterialApp orCupertinoApp and provide it with a Router configuration. Routing packages,such as go_router, typically provide aconfiguration for you. For example:Because packages like go_router are declarative, they will always display thesame screen(s) when a deep link is received.Note for advanced developers:  If you prefer not to use a routing package  and would like full control over navigation and routing in your app, override  RouteInformationParser and RouterDelegate. When the state in your app  changes, you can precisely control the stack of screens by providing a list of  Page objects using the Navigator.pages parameter. For more details, see the  Router API documentation.<topic_end><topic_start>Using Router and Navigator togetherThe Router and Navigator are designed to work together. You can navigateusing the Router API through a declarative routing package, such asgo_router, or by calling imperative methods such as push() and pop() onthe Navigator.When you navigate using the Router or a declarative routing package, eachroute on the Navigator is page-backed, meaning it was created from aPage using the pagesargument on the Navigator constructor. Conversely, any Routecreated by calling Navigator.push or showDialog will add a pagelessroute to the Navigator. If you are using a routing package, Routes that arepage-backed are always deep-linkable, whereas pageless routesare not.When a page-backed Route is removed from the Navigator, all of thepageless routes after it are also removed. For example, if a deep linknavigates by removing a page-backed route from the Navigator, all pageless_routes after (up until the next _page-backed route) are removed too.info Note  You can’t prevent navigation from page-backed screens using WillPopScope.  Instead, you should consult your routing package’s API documentation.<topic_end><topic_start>Web supportApps using the Router class integrate with the browser History API to providea consistent experience when using the browser’s back and forward buttons.Whenever you navigate using the Router, a History API entry is added to thebrowser’s history stack. Pressing the back button uses reversechronological navigation, meaning that the user is taken to the previouslyvisited location that was shown using the Router. This means that if the userpops a page from the Navigator and then presses the browser back buttonthe previous page is pushed back onto the stack.<topic_end><topic_start>More informationFor more information on navigation and routing, check out the followingresources:<topic_end><topic_start>Work with tabsWorking with tabs is a common pattern in apps that follow theMaterial Design guidelines.Flutter includes a convenient way to create tab layouts as part ofthe material library.This recipe creates a tabbed example using the following steps;<topic_end><topic_start>1. Create a TabControllerFor tabs to work, you need to keep the selected tab and contentsections in sync.This is the job of the TabController.Either create a TabController manually,or automatically by using a DefaultTabController widget.Using DefaultTabController is the simplest option, since itcreates a TabController and makes it available to all descendant widgets.<code_start>return MaterialApp(  home: DefaultTabController(    length: 3,    child: Scaffold(),  ),);<code_end><topic_end><topic_start>2. Create the tabsWhen a tab is selected, it needs to display content.You can create tabs using the TabBar widget.In this example, create a TabBar with threeTab widgets and place it within an AppBar.<code_start>return MaterialApp(  home: DefaultTabController(    length: 3,    child: Scaffold(      appBar: AppBar(        bottom: const TabBar(          tabs: [            Tab(icon: Icon(Icons.directions_car)),            Tab(icon: Icon(Icons.directions_transit)),            Tab(icon: Icon(Icons.directions_bike)),          ],        ),      ),    ),  ),);<code_end>By default, the TabBar looks up the widget tree for the nearestDefaultTabController. If you’re manually creating a TabController,pass it to the TabBar.<topic_end><topic_start>3. Create content for each tabNow that you have tabs, display content when a tab is selected.For this purpose, use the TabBarView widget.info Note  Order is important and must correspond to the order of the tabs in the  TabBar.<code_start>body: const TabBarView(  children: [    Icon(Icons.directions_car),    Icon(Icons.directions_transit),    Icon(Icons.directions_bike),  ],),<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() {  runApp(const TabBarDemo());}class TabBarDemo extends StatelessWidget {  const TabBarDemo({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      home: DefaultTabController(        length: 3,        child: Scaffold(          appBar: AppBar(            bottom: const TabBar(              tabs: [                Tab(icon: Icon(Icons.directions_car)),                Tab(icon: Icon(Icons.directions_transit)),                Tab(icon: Icon(Icons.directions_bike)),              ],            ),            title: const Text('Tabs Demo'),          ),          body: const TabBarView(            children: [              Icon(Icons.directions_car),              Icon(Icons.directions_transit),              Icon(Icons.directions_bike),            ],          ),        ),      ),    );  }}<code_end><topic_end><topic_start>Navigate to a new screen and backMost apps contain several screens for displaying different types ofinformation.For example, an app might have a screen that displays products.When the user taps the image of a product, a new screen displaysdetails about the product.Terminology: In Flutter, screens and pages are called routes.  The remainder of this recipe refers to routes.In Android, a route is equivalent to an Activity.In iOS, a route is equivalent to a ViewController.In Flutter, a route is just a widget.This recipe uses the Navigator to navigate to a new route.The next few sections show how to navigate between two routes,using these steps:<topic_end><topic_start>1. Create two routesFirst, create two routes to work with. Since this is a basic example,each route contains only a single button. Tapping the button on thefirst route navigates to the second route. Tapping the button on thesecond route returns to the first route.First, set up the visual structure:<code_start>class FirstRoute extends StatelessWidget {  const FirstRoute({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('First Route'),      ),      body: Center(        child: ElevatedButton(          child: const Text('Open route'),          onPressed: () {            // Navigate to second route when tapped.          },        ),      ),    );  }}class SecondRoute extends StatelessWidget {  const SecondRoute({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Second Route'),      ),      body: Center(        child: ElevatedButton(          onPressed: () {            // Navigate back to first route when tapped.          },          child: const Text('Go back!'),        ),      ),    );  }}<code_end><topic_end><topic_start>2. Navigate to the second route using Navigator.push()To switch to a new route, use the Navigator.push()method. The push() method adds a Route to the stack of routes managed bythe Navigator. Where does the Route come from?You can create your own, or use a MaterialPageRoute,which is useful because it transitions to thenew route using a platform-specific animation.In the build() method of the FirstRoute widget,update the onPressed() callback:<code_start>// Within the `FirstRoute` widgetonPressed: () {  Navigator.push(    context,    MaterialPageRoute(builder: (context) => const SecondRoute()),  );}<code_end><topic_end><topic_start>3. Return to the first route using Navigator.pop()How do you close the second route and return to the first?By using the Navigator.pop() method.The pop() method removes the current Route from the stack ofroutes managed by the Navigator.To implement a return to the original route, update the onPressed()callback in the SecondRoute widget:<code_start>// Within the SecondRoute widgetonPressed: () {  Navigator.pop(context);}<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() {  runApp(const MaterialApp(    title: 'Navigation Basics',    home: FirstRoute(),  ));}class FirstRoute extends StatelessWidget {  const FirstRoute({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('First Route'),      ),      body: Center(        child: ElevatedButton(          child: const Text('Open route'),          onPressed: () {            Navigator.push(              context,              MaterialPageRoute(builder: (context) => const SecondRoute()),            );          },        ),      ),    );  }}class SecondRoute extends StatelessWidget {  const SecondRoute({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Second Route'),      ),      body: Center(        child: ElevatedButton(          onPressed: () {            Navigator.pop(context);          },          child: const Text('Go back!'),        ),      ),    );  }}<code_end><topic_end><topic_start>Navigation with CupertinoPageRouteIn the previous example you learned how to navigate between screensusing the MaterialPageRoute from Material Components.However, in Flutter you are not limited to Material design language,instead, you also have access to Cupertino (iOS-style) widgets.Implementing navigation with Cupertino widgets follows the same stepsas when using MaterialPageRoute, but instead you use CupertinoPageRoutewhich provides an iOS-style transition animation.In the following example, these widgets have been replaced:This way, the example follows the current iOS design language.You don’t need to replace all Material widgets with Cupertino versions  to use CupertinoPageRoute  since Flutter allows you to mix and match Material and Cupertino widgets  depending on your needs.<code_start>import 'package:flutter/cupertino.dart';void main() {  runApp(const CupertinoApp(    title: 'Navigation Basics',    home: FirstRoute(),  ));}class FirstRoute extends StatelessWidget {  const FirstRoute({super.key});  @override  Widget build(BuildContext context) {    return CupertinoPageScaffold(      navigationBar: const CupertinoNavigationBar(        middle: Text('First Route'),      ),      child: Center(        child: CupertinoButton(          child: const Text('Open route'),          onPressed: () {            Navigator.push(              context,              CupertinoPageRoute(builder: (context) => const SecondRoute()),            );          },        ),      ),    );  }}class SecondRoute extends StatelessWidget {  const SecondRoute({super.key});  @override  Widget build(BuildContext context) {    return CupertinoPageScaffold(      navigationBar: const CupertinoNavigationBar(        middle: Text('Second Route'),      ),      child: Center(        child: CupertinoButton(          onPressed: () {            Navigator.pop(context);          },          child: const Text('Go back!'),        ),      ),    );  }}<code_end><topic_end><topic_start>Send data to a new screenOften, you not only want to navigate to a new screen,but also pass data to the screen as well.For example, you might want to pass information aboutthe item that’s been tapped.Remember: Screens are just widgets.In this example, create a list of todos.When a todo is tapped, navigate to a new screen (widget) thatdisplays information about the todo.This recipe uses the following steps:<topic_end><topic_start>1. Define a todo classFirst, you need a simple way to represent todos. For this example,create a class that contains two pieces of data: the title and description.<code_start>class Todo {  final String title;  final String description;  const Todo(this.title, this.description);}<code_end><topic_end><topic_start>2. Create a list of todosSecond, display a list of todos. In this example, generate20 todos and show them using a ListView.For more information on working with lists,see the Use lists recipe.<topic_end><topic_start>Generate the list of todos<code_start>final todos = List.generate(  20,  (i) => Todo(    'Todo $i',    'A description of what needs to be done for Todo $i',  ),);<code_end><topic_end><topic_start>Display the list of todos using a ListView<code_start>ListView.builder(  itemCount: todos.length,  itemBuilder: (context, index) {    return ListTile(      title: Text(todos[index].title),    );  },),<code_end>So far, so good.This generates 20 todos and displays them in a ListView.<topic_end><topic_start>3. Create a Todo screen to display the listFor this, we create a StatelessWidget. We call it TodosScreen.Since the contents of this page won’t change during runtime,we’ll have to require the listof todos within the scope of this widget.We pass in our ListView.builder as body of the widget we’re returning to build().This’ll render the list on to the screen for you to get going!<code_start>class TodosScreen extends StatelessWidget {  // Requiring the list of todos.  const TodosScreen({super.key, required this.todos});  final List<Todo> todos;  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Todos'),      ),      //passing in the ListView.builder      body: ListView.builder(        itemCount: todos.length,        itemBuilder: (context, index) {          return ListTile(            title: Text(todos[index].title),          );        },      ),    );  }}<code_end>With Flutter’s default styling, you’re good to go without sweating about things that you’d like to do later on!<topic_end><topic_start>4. Create a detail screen to display information about a todoNow, create the second screen. The title of the screen contains thetitle of the todo, and the body of the screen shows the description.Since the detail screen is a normal StatelessWidget,require the user to enter a Todo in the UI.Then, build the UI using the given todo.<code_start>class DetailScreen extends StatelessWidget {  // In the constructor, require a Todo.  const DetailScreen({super.key, required this.todo});  // Declare a field that holds the Todo.  final Todo todo;  @override  Widget build(BuildContext context) {    // Use the Todo to create the UI.    return Scaffold(      appBar: AppBar(        title: Text(todo.title),      ),      body: Padding(        padding: const EdgeInsets.all(16),        child: Text(todo.description),      ),    );  }}<code_end><topic_end><topic_start>5. Navigate and pass data to the detail screenWith a DetailScreen in place,you’re ready to perform the Navigation.In this example, navigate to the DetailScreen when a usertaps a todo in the list. Pass the todo to the DetailScreen.To capture the user’s tap in the TodosScreen, write an onTap()callback for the ListTile widget. Within the onTap() callback,use the Navigator.push() method.<code_start>body: ListView.builder(  itemCount: todos.length,  itemBuilder: (context, index) {    return ListTile(      title: Text(todos[index].title),      // When a user taps the ListTile, navigate to the DetailScreen.      // Notice that you're not only creating a DetailScreen, you're      // also passing the current todo through to it.      onTap: () {        Navigator.push(          context,          MaterialPageRoute(            builder: (context) => DetailScreen(todo: todos[index]),          ),        );      },    );  },),<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';class Todo {  final String title;  final String description;  const Todo(this.title, this.description);}void main() {  runApp(    MaterialApp(      title: 'Passing Data',      home: TodosScreen(        todos: List.generate(          20,          (i) => Todo(            'Todo $i',            'A description of what needs to be done for Todo $i',          ),        ),      ),    ),  );}class TodosScreen extends StatelessWidget {  const TodosScreen({super.key, required this.todos});  final List<Todo> todos;  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Todos'),      ),      body: ListView.builder(        itemCount: todos.length,        itemBuilder: (context, index) {          return ListTile(            title: Text(todos[index].title),            // When a user taps the ListTile, navigate to the DetailScreen.            // Notice that you're not only creating a DetailScreen, you're            // also passing the current todo through to it.            onTap: () {              Navigator.push(                context,                MaterialPageRoute(                  builder: (context) => DetailScreen(todo: todos[index]),                ),              );            },          );        },      ),    );  }}class DetailScreen extends StatelessWidget {  // In the constructor, require a Todo.  const DetailScreen({super.key, required this.todo});  // Declare a field that holds the Todo.  final Todo todo;  @override  Widget build(BuildContext context) {    // Use the Todo to create the UI.    return Scaffold(      appBar: AppBar(        title: Text(todo.title),      ),      body: Padding(        padding: const EdgeInsets.all(16),        child: Text(todo.description),      ),    );  }}<code_end><topic_end><topic_start>Alternatively, pass the arguments using RouteSettingsRepeat the first two steps.<topic_end><topic_start>Create a detail screen to extract the argumentsNext, create a detail screen that extracts and displays the title and description from the Todo. To access the Todo, use the ModalRoute.of() method. This method returns the current route with the arguments.<code_start>class DetailScreen extends StatelessWidget {  const DetailScreen({super.key});  @override  Widget build(BuildContext context) {    final todo = ModalRoute.of(context)!.settings.arguments as Todo;    // Use the Todo to create the UI.    return Scaffold(      appBar: AppBar(        title: Text(todo.title),      ),      body: Padding(        padding: const EdgeInsets.all(16),        child: Text(todo.description),      ),    );  }}<code_end><topic_end><topic_start>Navigate and pass the arguments to the detail screenFinally, navigate to the DetailScreen when a user tapsa ListTile widget using Navigator.push().Pass the arguments as part of the RouteSettings.The DetailScreen extracts these arguments.<code_start>ListView.builder(  itemCount: todos.length,  itemBuilder: (context, index) {    return ListTile(      title: Text(todos[index].title),      // When a user taps the ListTile, navigate to the DetailScreen.      // Notice that you're not only creating a DetailScreen, you're      // also passing the current todo through to it.      onTap: () {        Navigator.push(          context,          MaterialPageRoute(            builder: (context) => const DetailScreen(),            // Pass the arguments as part of the RouteSettings. The            // DetailScreen reads the arguments from these settings.            settings: RouteSettings(              arguments: todos[index],            ),          ),        );      },    );  },)<code_end><topic_end><topic_start>Complete example<code_start>import 'package:flutter/material.dart';class Todo {  final String title;  final String description;  const Todo(this.title, this.description);}void main() {  runApp(    MaterialApp(      title: 'Passing Data',      home: TodosScreen(        todos: List.generate(          20,          (i) => Todo(            'Todo $i',            'A description of what needs to be done for Todo $i',          ),        ),      ),    ),  );}class TodosScreen extends StatelessWidget {  const TodosScreen({super.key, required this.todos});  final List<Todo> todos;  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Todos'),      ),      body: ListView.builder(        itemCount: todos.length,        itemBuilder: (context, index) {          return ListTile(            title: Text(todos[index].title),            // When a user taps the ListTile, navigate to the DetailScreen.            // Notice that you're not only creating a DetailScreen, you're            // also passing the current todo through to it.            onTap: () {              Navigator.push(                context,                MaterialPageRoute(                  builder: (context) => const DetailScreen(),                  // Pass the arguments as part of the RouteSettings. The                  // DetailScreen reads the arguments from these settings.                  settings: RouteSettings(                    arguments: todos[index],                  ),                ),              );            },          );        },      ),    );  }}class DetailScreen extends StatelessWidget {  const DetailScreen({super.key});  @override  Widget build(BuildContext context) {    final todo = ModalRoute.of(context)!.settings.arguments as Todo;    // Use the Todo to create the UI.    return Scaffold(      appBar: AppBar(        title: Text(todo.title),      ),      body: Padding(        padding: const EdgeInsets.all(16),        child: Text(todo.description),      ),    );  }}<code_end><topic_end><topic_start>Return data from a screenIn some cases, you might want to return data from a new screen.For example, say you push a new screen that presents two options to a user.When the user taps an option, you want to inform the first screenof the user’s selection so that it can act on that information.You can do this with the Navigator.pop()method using the following steps:<topic_end><topic_start>1. Define the home screenThe home screen displays a button. When tapped,it launches the selection screen.<code_start>class HomeScreen extends StatelessWidget {  const HomeScreen({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Returning Data Demo'),      ),      // Create the SelectionButton widget in the next step.      body: const Center(        child: SelectionButton(),      ),    );  }}<code_end><topic_end><topic_start>2. Add a button that launches the selection screenNow, create the SelectionButton, which does the following:<code_start>class SelectionButton extends StatefulWidget {  const SelectionButton({super.key});  @override  State<SelectionButton> createState() => _SelectionButtonState();}class _SelectionButtonState extends State<SelectionButton> {  @override  Widget build(BuildContext context) {    return ElevatedButton(      onPressed: () {        _navigateAndDisplaySelection(context);      },      child: const Text('Pick an option, any option!'),    );  }  Future<void> _navigateAndDisplaySelection(BuildContext context) async {    // Navigator.push returns a Future that completes after calling    // Navigator.pop on the Selection Screen.    final result = await Navigator.push(      context,      // Create the SelectionScreen in the next step.      MaterialPageRoute(builder: (context) => const SelectionScreen()),    );  }}<code_end><topic_end><topic_start>3. Show the selection screen with two buttonsNow, build a selection screen that contains two buttons.When a user taps a button,that app closes the selection screen and lets the homescreen know which button was tapped.This step defines the UI.The next step adds code to return data.<code_start>class SelectionScreen extends StatelessWidget {  const SelectionScreen({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Pick an option'),      ),      body: Center(        child: Column(          mainAxisAlignment: MainAxisAlignment.center,          children: [            Padding(              padding: const EdgeInsets.all(8),              child: ElevatedButton(                onPressed: () {                  // Pop here with "Yep"...                },                child: const Text('Yep!'),              ),            ),            Padding(              padding: const EdgeInsets.all(8),              child: ElevatedButton(                onPressed: () {                  // Pop here with "Nope"...                },                child: const Text('Nope.'),              ),            )          ],        ),      ),    );  }}<code_end><topic_end><topic_start>4. When a button is tapped, close the selection screenNow, update the onPressed() callback for both of the buttons.To return data to the first screen,use the Navigator.pop() method,which accepts an optional second argument called result.Any result is returned to the Future in the SelectionButton.<topic_end><topic_start>Yep button<code_start>ElevatedButton(  onPressed: () {    // Close the screen and return "Yep!" as the result.    Navigator.pop(context, 'Yep!');  },  child: const Text('Yep!'),)<code_end><topic_end><topic_start>Nope button<code_start>ElevatedButton(  onPressed: () {    // Close the screen and return "Nope." as the result.    Navigator.pop(context, 'Nope.');  },  child: const Text('Nope.'),)<code_end><topic_end><topic_start>5. Show a snackbar on the home screen with the selectionNow that you’re launching a selection screen and awaiting the result,you’ll want to do something with the information that’s returned.In this case, show a snackbar displaying the result by using the_navigateAndDisplaySelection() method in SelectionButton:<code_start>// A method that launches the SelectionScreen and awaits the result from// Navigator.pop.Future<void> _navigateAndDisplaySelection(BuildContext context) async {  // Navigator.push returns a Future that completes after calling  // Navigator.pop on the Selection Screen.  final result = await Navigator.push(    context,    MaterialPageRoute(builder: (context) => const SelectionScreen()),  );  // When a BuildContext is used from a StatefulWidget, the mounted property  // must be checked after an asynchronous gap.  if (!context.mounted) return;  // After the Selection Screen returns a result, hide any previous snackbars  // and show the new result.  ScaffoldMessenger.of(context)    ..removeCurrentSnackBar()    ..showSnackBar(SnackBar(content: Text('$result')));}<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() {  runApp(    const MaterialApp(      title: 'Returning Data',      home: HomeScreen(),    ),  );}class HomeScreen extends StatelessWidget {  const HomeScreen({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Returning Data Demo'),      ),      body: const Center(        child: SelectionButton(),      ),    );  }}class SelectionButton extends StatefulWidget {  const SelectionButton({super.key});  @override  State<SelectionButton> createState() => _SelectionButtonState();}class _SelectionButtonState extends State<SelectionButton> {  @override  Widget build(BuildContext context) {    return ElevatedButton(      onPressed: () {        _navigateAndDisplaySelection(context);      },      child: const Text('Pick an option, any option!'),    );  }  // A method that launches the SelectionScreen and awaits the result from  // Navigator.pop.  Future<void> _navigateAndDisplaySelection(BuildContext context) async {    // Navigator.push returns a Future that completes after calling    // Navigator.pop on the Selection Screen.    final result = await Navigator.push(      context,      MaterialPageRoute(builder: (context) => const SelectionScreen()),    );    // When a BuildContext is used from a StatefulWidget, the mounted property    // must be checked after an asynchronous gap.    if (!context.mounted) return;    // After the Selection Screen returns a result, hide any previous snackbars    // and show the new result.    ScaffoldMessenger.of(context)      ..removeCurrentSnackBar()      ..showSnackBar(SnackBar(content: Text('$result')));  }}class SelectionScreen extends StatelessWidget {  const SelectionScreen({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Pick an option'),      ),      body: Center(        child: Column(          mainAxisAlignment: MainAxisAlignment.center,          children: <Widget>[            Padding(              padding: const EdgeInsets.all(8),              child: ElevatedButton(                onPressed: () {                  // Close the screen and return "Yep!" as the result.                  Navigator.pop(context, 'Yep!');                },                child: const Text('Yep!'),              ),            ),            Padding(              padding: const EdgeInsets.all(8),              child: ElevatedButton(                onPressed: () {                  // Close the screen and return "Nope." as the result.                  Navigator.pop(context, 'Nope.');                },                child: const Text('Nope.'),              ),            )          ],        ),      ),    );  }}<code_end><topic_end><topic_start>Add a drawer to a screenIn apps that use Material Design,there are two primary options for navigation: tabs and drawers.When there is insufficient space to support tabs,drawers provide a handy alternative.In Flutter, use the Drawer widget in combination with aScaffold to create a layout with a Material Design drawer.This recipe uses the following steps:<topic_end><topic_start>1. Create a ScaffoldTo add a drawer to the app, wrap it in a Scaffold widget.The Scaffold widget provides a consistent visual structure to apps thatfollow the Material Design Guidelines.It also supports special Material Designcomponents, such as Drawers, AppBars, and SnackBars.In this example, create a Scaffold with a drawer:<code_start>Scaffold(  appBar: AppBar(    title: const Text('AppBar without hamburger button'),  ),  drawer: // Add a Drawer here in the next step.);<code_end><topic_end><topic_start>2. Add a drawerNow add a drawer to the Scaffold. A drawer can be any widget,but it’s often best to use the Drawer widget from thematerial library,which adheres to the Material Design spec.<code_start>Scaffold(  appBar: AppBar(    title: const Text('AppBar with hamburger button'),  ),  drawer: Drawer(    child: // Populate the Drawer in the next step.  ),);<code_end><topic_end><topic_start>3. Populate the drawer with itemsNow that you have a Drawer in place, add content to it.For this example, use a ListView.While you could use a Column widget,ListView is handy because it allows users to scrollthrough the drawer if thecontent takes more space than the screen supports.Populate the ListView with a DrawerHeaderand two ListTile widgets.For more information on working with Lists,see the list recipes.<code_start>Drawer(  // Add a ListView to the drawer. This ensures the user can scroll  // through the options in the drawer if there isn't enough vertical  // space to fit everything.  child: ListView(    // Important: Remove any padding from the ListView.    padding: EdgeInsets.zero,    children: [      const DrawerHeader(        decoration: BoxDecoration(          color: Colors.blue,        ),        child: Text('Drawer Header'),      ),      ListTile(        title: const Text('Item 1'),        onTap: () {          // Update the state of the app.          // ...        },      ),      ListTile(        title: const Text('Item 2'),        onTap: () {          // Update the state of the app.          // ...        },      ),    ],  ),);<code_end><topic_end><topic_start>4. Close the drawer programmaticallyAfter a user taps an item, you might want to close the drawer.You can do this by using the Navigator.When a user opens the drawer, Flutter adds the drawer to the navigationstack. Therefore, to close the drawer, call Navigator.pop(context).<code_start>ListTile(  title: const Text('Item 1'),  onTap: () {    // Update the state of the app    // ...    // Then close the drawer    Navigator.pop(context);  },),<code_end><topic_end><topic_start>Interactive exampleThis example shows a Drawer as it is used within a Scaffold widget.The Drawer has three ListTile items.The _onItemTapped function changes the selected item’s indexand displays the corresponding text in the center of the Scaffold.info Note  For more information on implementing navigation,  check out the Navigation section of the cookbook.<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  static const appTitle = 'Drawer Demo';  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: appTitle,      home: MyHomePage(title: appTitle),    );  }}class MyHomePage extends StatefulWidget {  const MyHomePage({super.key, required this.title});  final String title;  @override  State<MyHomePage> createState() => _MyHomePageState();}class _MyHomePageState extends State<MyHomePage> {  int _selectedIndex = 0;  static const TextStyle optionStyle =      TextStyle(fontSize: 30, fontWeight: FontWeight.bold);  static const List<Widget> _widgetOptions = <Widget>[    Text(      'Index 0: Home',      style: optionStyle,    ),    Text(      'Index 1: Business',      style: optionStyle,    ),    Text(      'Index 2: School',      style: optionStyle,    ),  ];  void _onItemTapped(int index) {    setState(() {      _selectedIndex = index;    });  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: Text(widget.title)),      body: Center(        child: _widgetOptions[_selectedIndex],      ),      drawer: Drawer(        // Add a ListView to the drawer. This ensures the user can scroll        // through the options in the drawer if there isn't enough vertical        // space to fit everything.        child: ListView(          // Important: Remove any padding from the ListView.          padding: EdgeInsets.zero,          children: [            const DrawerHeader(              decoration: BoxDecoration(                color: Colors.blue,              ),              child: Text('Drawer Header'),            ),            ListTile(              title: const Text('Home'),              selected: _selectedIndex == 0,              onTap: () {                // Update the state of the app                _onItemTapped(0);                // Then close the drawer                Navigator.pop(context);              },            ),            ListTile(              title: const Text('Business'),              selected: _selectedIndex == 1,              onTap: () {                // Update the state of the app                _onItemTapped(1);                // Then close the drawer                Navigator.pop(context);              },            ),            ListTile(              title: const Text('School'),              selected: _selectedIndex == 2,              onTap: () {                // Update the state of the app                _onItemTapped(2);                // Then close the drawer                Navigator.pop(context);              },            ),          ],        ),      ),    );  }}<code_end><topic_end><topic_start>Deep linkingFlutter supports deep linking on iOS, Android, and web browsers.Opening a URL displays that screen in your app. With the followingsteps, you can launch and display routes by using named routes(either with the routes parameter oronGenerateRoute), or byusing the Router widget.info Note  Named routes are no longer recommended for most  applications. For more information, see  Limitations in the navigation overview page.If you’re running the app in a web browser, there’s no additional setuprequired. Route paths are handled in the same way as an iOS or Android deeplink. By default, web apps read the deep link path from the url fragment usingthe pattern: /#/path/to/app/screen, but this can be changed byconfiguring the URL strategy for your app.If you are a visual learner, check out the following video:Deep linking in Flutter<topic_end><topic_start>Get startedTo get started, see our cookbooks for Android and iOS:<topic_end><topic_start>Migrating from plugin-based deep linkingIf you have written a plugin to handle deep links, as described inDeep Links and Flutter applications(a free article on Medium),it will continue to work until you opt in to this behavior by addingFlutterDeepLinkingEnabled to Info.plist orflutter_deeplinking_enabled to AndroidManifest.xml, respectively.<topic_end><topic_start>BehaviorThe behavior varies slightly based on the platform and whether the app islaunched and running.When using the Router widget,your app has the ability to replace thecurrent set of pages when a new deep linkis opened while the app is running.<topic_end><topic_start>To learn more<topic_end><topic_start>Set up app links for AndroidDeep linking is a mechanism for launching an app with a URI.This URI contains scheme, host, and path,and opens the app to a specific screen.A app link is a type of deep link that useshttp or https and is exclusive to Android devices.Setting up app links requires one to own a web domain.Otherwise, consider using Firebase Hostingor GitHub Pages as a temporary solution.<topic_end><topic_start>1. Customize a Flutter applicationWrite a Flutter app that can handle an incoming URL.This example uses the go_router package to handle the routing.The Flutter team maintains the go_router package.It provides a simple API to handle complex routing scenarios.To create a new application, type flutter create <app-name>:To include go_router package in your app,add a dependency for go_router to the project:To add the go_router package as a dependency, run flutter pub add:To handle the routing,create a GoRouter object in the main.dart file:<code_start>import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; void main() => runApp(MaterialApp.router(routerConfig: router)); /// This handles '/' and '/details'. final router = GoRouter(   routes: [     GoRoute(       path: '/',       builder: (_, __) => Scaffold(         appBar: AppBar(title: const Text('Home Screen')),       ),       routes: [         GoRoute(           path: 'details',           builder: (_, __) => Scaffold(             appBar: AppBar(title: const Text('Details Screen')),           ),         ),       ],     ),   ], );<code_end><topic_end><topic_start>2. Modify AndroidManifest.xmlAdd the following metadata tag and intent filter inside the<activity> tag with .MainActivity.Replace example.com with your own web domain.info Note  The metadata tag flutter_deeplinking_enabled opts  into Flutter’s default deeplink handler.  If you are using the third-party plugins,  such as uni_links, setting this metadata tag will  break these plugins. Omit this metadata tag  if you prefer to use third-party plugins.<topic_end><topic_start>3. Hosting assetlinks.json fileHost an assetlinks.json file in using a web serverwith a domain that you own. This file tells themobile browser which Android application to open insteadof the browser. To create the file,get the package name of the Flutter app you created inthe previous step and the sha256 fingerprint of thesigning key you will be using to build the APK.<topic_end><topic_start>Package nameLocate the package name in AndroidManifest.xml,the package property under <manifest> tag.Package names are usually in the format of com.example.*.<topic_end><topic_start>sha256 fingerprintThe process might differ depending on how the apk is signed.<topic_end><topic_start>Using google play app signingYou can find the sha256 fingerprint directly from playdeveloper console. Open your app in the play console,under Release> Setup > App Integrity> App Signing tab:<topic_end><topic_start>Using local keystoreIf you are storing the key locally,you can generate sha256 using the following command:<topic_end><topic_start>assetlinks.jsonThe hosted file should look similar to this:Set the package_name value to your Android application ID.Set sha256_cert_fingerprints to the value you gotfrom the previous step.Host the file at a URL that resembles the following:<webdomain>/.well-known/assetlinks.jsonVerify that your browser can access this file.<topic_end><topic_start>TestingYou can use a real device or the Emulator to test an app link,but first make sure you have executed flutter run at least once onthe devices. This ensures that the Flutter application is installed.To test only the app setup, use the adb command:info Note   This doesn’t test whether the web files are   hosted correctly,   the command launches the app even   if web files are not presented.To test both web and app setup, you must click a linkdirectly through web browser or another app.One way is to create a Google Doc, add the link, and tap on it.If everything is set up correctly, the Flutter applicationlaunches and displays the details screen:<topic_end><topic_start>AppendixSource code: deeplink_cookbook<topic_end><topic_start>Set up universal links for iOSDeep linking is a mechanism for launching an app with a URI. This URIcontains scheme, host, and path, and opens the app to a specificscreen.A universal link is a type of deep link that uses http or httpsand is exclusive to Apple devices.Setting up universal links requires one to own a web domain.Otherwise, consider using Firebase Hosting or GitHub Pagesas a temporary solution.<topic_end><topic_start>1. Customize a Flutter applicationWrite a Flutter app that can handle an incoming URL.This example uses the go_router package to handle the routing.The Flutter team maintains the go_router package.It provides a simple API to handle complex routing scenarios.To create a new application, type flutter create <app-name>.To include the go_router package as a dependency,run flutter pub add:To handle the routing, create a GoRouter object in the main.dart file:<code_start>import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; void main() => runApp(MaterialApp.router(routerConfig: router)); /// This handles '/' and '/details'. final router = GoRouter(   routes: [     GoRoute(       path: '/',       builder: (_, __) => Scaffold(         appBar: AppBar(title: const Text('Home Screen')),       ),       routes: [         GoRoute(           path: 'details',           builder: (_, __) => Scaffold(             appBar: AppBar(title: const Text('Details Screen')),           ),         ),       ],     ),   ], );<code_end><topic_end><topic_start>2. Adjust iOS build settingsNavigate to the Info Plist file in the ios/Runner folder.Update the key to FlutterDeepLinkingEnabled with a Boolean value set to YES.info Note    The FlutterDeepLinkingEnabled property opts into Flutter’s default deeplink handler. If    you are using the third-party plugins, such as uni_links, setting this property will    break these plugins. Skip this step if you prefer to use third-party plugins.Click Associated Domains.Enter applinks:<web domain>. Replace <web domain> with your own domain name.You have finished configuring the application for deep linking.<topic_end><topic_start>3. Hosting apple-app-site-association fileYou need to host an apple-app-site-association file in the web domain.This file tells the mobile browser which iOS application to open instead of the browser.To create the file, get the app ID of the Flutter app you created in the previous step.<topic_end><topic_start>App IDApple formats the app ID as <team id>.<bundle id>.For example: Given a team ID of S8QB4VV633and a bundle ID of com.example.deeplinkCookbook, The app ID isS8QB4VV633.com.example.deeplinkCookbook.<topic_end><topic_start>apple-app-site-associationThe hosted file should have the following content:Set the appID value to <team id>.<bundle id>.Set the paths value to ["*"].The paths field specifies the allowed universal links.Using the asterisk, * redirects every path to the Flutter application.If needed, change the paths value to a setting more appropriateto your app.Host the file at a URL that resembles the following:<webdomain>/.well-known/apple-app-site-associationVerify that your browser can access this file.<topic_end><topic_start>Testinginfo Note  It might take up to 24 hours before Apple’s  Content Delivery Network (CDN)  requests the apple-app-site-association (AASA) file from your web domain.  The universal link won’t work until the CDN requests the file.  To bypass Apple’s CDN, check out the alternate mode section.You can use a real device or the Simulator to test a universal link,but first make sure you have executed flutter run at least once onthe devices. This ensures that the Flutter application is installed.If using the Simulator, test using the Xcode CLI:Otherwise, type the URL in the Note app and click it.If everything is set up correctly, the Flutter applicationlaunches and displays the details screen:<topic_end><topic_start>AppendixSource code: deeplink_cookbook<topic_end><topic_start>Configuring the URL strategy on the webFlutter web apps support two ways of configuringURL-based navigation on the web:<topic_end><topic_start>Configuring the URL strategyTo configure Flutter to use the path instead, use theusePathUrlStrategy function provided by the flutter_web_plugins libraryin the SDK:<topic_end><topic_start>Configuring your web serverPathUrlStrategy uses the History API, which requires additionalconfiguration for web servers.To configure your web server to support PathUrlStrategy, check your web server’sdocumentation to rewrite requests to index.html.Check your web server’sdocumentation for details on how to configure single-page apps.If you are using Firebase Hosting, choose the “Configure as a single-page app”option when initializing your project. For more information see Firebase’sConfigure rewrites documentation.The local dev server created by running flutter run -d chrome is configured tohandle any path gracefully and fallback to your app’s index.html file.<topic_end><topic_start>Hosting a Flutter app at a non-root locationUpdate the <base href="/"> tag in web/index.htmlto the path where your app is hosted.For example, to host your Flutter app atmy_app.dev/flutter_app, changethis tag to <base href="/flutter_app/">.<topic_end><topic_start>Introduction to animationsWell-designed animations make a UI feel more intuitive,contribute to the slick look and feel of a polished app,and improve the user experience.Flutter’s animation support makes it easy to implement a variety ofanimation types. Many widgets, especially Material widgets,come with the standard motion effects defined in their design spec,but it’s also possible to customize these effects.<topic_end><topic_start>Choosing an approachThere are different approaches you can take when creatinganimations in Flutter. Which approach is right for you?To help you decide, check out the video,How to choose which Flutter Animation Widget is right for you?(Also published as a companion article.)(To dive deeper into the decision process,watch the Animations in Flutter done right video,presented at Flutter Europe.)As shown in the video, the followingdecision tree helps you decide what approachto use when implementing a Flutter animation:If a pre-packaged implicit animation (the easiest animationto implement) suits your needs, watchAnimation basics with implicit animations.(Also published as a companion article.)Learn about Animation Basics with Implicit AnimationsTo create a custom implicit animation, watchCreating your own custom implicit animations with TweenAnimationBuilder.(Also published as a companion article.)Learn about building Custom Implicit Animations with TweenAnimationBuilderTo create an explicit animation (where you control the animation,rather than letting the framework control it), perhapsyou can use one of the built-in explicit animations classes.For more information, watchMaking your first directional animations withbuilt-in explicit animations.(Also published as a companion article.)If you need to build an explicit animation from scratch, watchCreating custom explicit animations withAnimatedBuilder and AnimatedWidget.(Also published as a companion article.)For a deeper understanding of just how animations work in Flutter, watchAnimation deep dive.(Also published as a companion article.)<topic_end><topic_start>Codelabs, tutorials, and articlesThe following resources are a good place to start learningthe Flutter animation framework. Each of these documentsshows how to write animation code.Implicit animations codelabCovers how to use implicit animationsusing step-by-step instructions and interactive examples.Animations tutorialExplains the fundamental classes in the Flutter animation package(controllers, Animatable, curves, listeners, builders),as it guides you through a progression of tween animations usingdifferent aspects of the animation APIs. This tutorial showshow to create your own custom explicit animations.Zero to One with Flutter, part 1 and part 2Medium articles showing how to create an animated chart using tweening.Write your first Flutter app on the webCodelab demonstrating how to create a formthat uses animation to show the user’s progressas they fill in the fields.<topic_end><topic_start>Animation typesGenerally, animations are either tween- or physics-based.The following sections explain what these terms mean,and point you to resources where you can learn more.<topic_end><topic_start>Tween animationShort for in-betweening. In a tween animation, the beginningand ending points are defined, as well as a timeline, and a curvethat defines the timing and speed of the transition.The framework calculates how to transition from the beginning pointto the end point.The documents listed above, such as theAnimations tutorial, are not specificallyabout tweening, but they use tweens in their examples.<topic_end><topic_start>Physics-based animationIn physics-based animation, motion is modeled to resemble real-worldbehavior. When you toss a ball, for example, where and when it landsdepends on how fast it was tossed and how far it was from the ground.Similarly, dropping a ball attached to a spring falls(and bounces) differently than dropping a ball attached to a string.Animate a widget using a physics simulationA recipe in the animations section of the Flutter cookbook.Also see the API documentation forAnimationController.animateWith andSpringSimulation.<topic_end><topic_start>Pre-canned animationsIf you are using Material widgets, you might checkout the animations package available on pub.dev.This package contains pre-built animations forthe following commonly used patterns:Container transforms, shared axis transitions,fade through transitions, and fade transitions.<topic_end><topic_start>Common animation patternsMost UX or motion designers find that certainanimation patterns are used repeatedly when designing a UI.This section lists some of the commonlyused animation patterns, and tells you where to learn more.<topic_end><topic_start>Animated list or gridThis pattern involves animating the addition or removal ofelements from a list or grid.<topic_end><topic_start>Shared element transitionIn this pattern, the user selects an element—often animage—from the page, and the UI animates the selected elementto a new page with more detail. In Flutter, you can easily implementshared element transitions between routes (pages)using the Hero widget.<topic_end><topic_start>Staggered animationAnimations that are broken into smaller motions,where some of the motion is delayed.The smaller animations might be sequential,or might partially or completely overlap.<topic_end><topic_start>Other resourcesLearn more about Flutter animations at the following links:Animation samples from the Sample app catalog.Animation recipes from the Flutter cookbook.Animation videos from the Flutter YouTube channel.Animations: overviewA look at some of the major classes in theanimations library, and Flutter’s animation architecture.Animation and motion widgetsA catalog of some of the animation widgetsprovided in the Flutter APIs.The animation library in the Flutter API documentationThe animation API for the Flutter framework. This linktakes you to a technical overview page for the library.<topic_end><topic_start>Animations tutorial<topic_end><topic_start>What you'll learnThis tutorial shows you how to build explicit animations in Flutter.After introducing some of the essential concepts, classes,and methods in the animation library, it walks you through 5animation examples. The examples build on each other,introducing you to different aspects of the animation library.The Flutter SDK also provides built-in explicit animations,such as FadeTransition, SizeTransition,and SlideTransition. These simple animations aretriggered by setting a beginning and ending point.They are simpler to implementthan custom explicit animations, which are described here.<topic_end><topic_start>Essential animation concepts and classes<topic_end><topic_start>What's the point?The animation system in Flutter is based on typedAnimation objects. Widgets can either incorporatethese animations in their build functions directly byreading their current value and listening to their statechanges or they can use the animations as the basis ofmore elaborate animations that they pass along toother widgets.<topic_end><topic_start>Animation<double>In Flutter, an Animation object knows nothing about whatis onscreen. An Animation is an abstract class thatunderstands its current value and its state (completed or dismissed).One of the more commonly used animation types is Animation<double>.An Animation object sequentially generatesinterpolated numbers between two values over a certain duration.The output of an Animation object might be linear,a curve, a step function, or any other mapping you can devise.Depending on how the Animation object is controlled,it could run in reverse, or even switch directions in themiddle.Animations can also interpolate types other than double, such asAnimation<Color> or Animation<Size>.An Animation object has state. Its current value isalways available in the .value member.An Animation object knows nothing about rendering orbuild() functions.<topic_end><topic_start>Curved­AnimationA CurvedAnimation defines the animation’s progressas a non-linear curve.<code_start>animation = CurvedAnimation(parent: controller, curve: Curves.easeIn);<code_end>info Note  The Curves class defines many commonly used curves,  or you can create your own. For example:Browse the Curves documentation for a complete listing  (with visual previews) of the Curves constants that ship with Flutter.CurvedAnimation and AnimationController (described in the next section)are both of type Animation<double>, so you can pass them interchangeably.The CurvedAnimation wraps the object it’s modifying—youdon’t subclass AnimationController to implement a curve.<topic_end><topic_start>Animation­ControllerAnimationController is a special Animationobject that generates a new value whenever the hardwareis ready for a new frame. By default,an AnimationController linearly produces the numbersfrom 0.0 to 1.0 during a given duration.For example, this code creates an Animation object,but does not start it running:<code_start>controller =    AnimationController(duration: const Duration(seconds: 2), vsync: this);<code_end>AnimationController derives from Animation<double>, so it can be usedwherever an Animation object is needed. However, the AnimationControllerhas additional methods to control the animation. For example, you startan animation with the .forward() method. The generation of numbers istied to the screen refresh, so typically 60 numbers are generated persecond. After each number is generated, each Animation object calls theattached Listener objects. To create a custom display list for eachchild, see RepaintBoundary.When creating an AnimationController, you pass it a vsync argument.The presence of vsync prevents offscreen animations from consumingunnecessary resources.You can use your stateful object as the vsync by addingSingleTickerProviderStateMixin to the class definition.You can see an example of this in animate1 on GitHub.info Note  In some cases, a position might exceed the AnimationController’s  0.0-1.0 range. For example, the fling() function  allows you to provide velocity, force, and position  (via the Force object). The position can be anything and  so can be outside of the 0.0 to 1.0 range.A CurvedAnimation can also exceed the 0.0 to 1.0 range,  even if the AnimationController doesn’t.  Depending on the curve selected, the output of  the CurvedAnimation can have a wider range than the input.  For example, elastic curves such as Curves.elasticIn  significantly overshoots or undershoots the default range.<topic_end><topic_start>TweenBy default, the AnimationController object ranges from 0.0 to 1.0.If you need a different range or a different data type, you can use aTween to configure an animation to interpolate to adifferent range or data type. For example, thefollowing Tween goes from -200.0 to 0.0:<code_start>tween = Tween<double>(begin: -200, end: 0);<code_end>A Tween is a stateless object that takes only begin and end.The sole job of a Tween is to define a mapping from aninput range to an output range. The input range is commonly0.0 to 1.0, but that’s not a requirement.A Tween inherits from Animatable<T>, not from Animation<T>.An Animatable, like Animation, doesn’t have to output double.For example, ColorTween specifies a progression between two colors.<code_start>colorTween = ColorTween(begin: Colors.transparent, end: Colors.black54);<code_end>A Tween object doesn’t store any state. Instead, it provides theevaluate(Animation<double> animation) method that uses the transform function to map the current value of the animation(between 0.0 and 1.0), to the actual animation value.The current value of the Animation object can be found in the.value method. The evaluate function also performs some housekeeping,such as ensuring that begin and end are returned when theanimation values are 0.0 and 1.0, respectively.<topic_end><topic_start>Tween.animateTo use a Tween object, call animate() on the Tween,passing in the controller object. For example,the following code generates theinteger values from 0 to 255 over the course of 500 ms.<code_start>AnimationController controller = AnimationController(    duration: const Duration(milliseconds: 500), vsync: this);Animation<int> alpha = IntTween(begin: 0, end: 255).animate(controller);<code_end>info Note  The animate() method returns an Animation,  not an Animatable.The following example shows a controller, a curve, and a Tween:<code_start>AnimationController controller = AnimationController(    duration: const Duration(milliseconds: 500), vsync: this);final Animation<double> curve =    CurvedAnimation(parent: controller, curve: Curves.easeOut);Animation<int> alpha = IntTween(begin: 0, end: 255).animate(curve);<code_end><topic_end><topic_start>Animation notificationsAn Animation object can have Listeners and StatusListeners,defined with addListener() and addStatusListener().A Listener is called whenever the value of the animation changes.The most common behavior of a Listener is to call setState()to cause a rebuild. A StatusListener is called when an animation begins,ends, moves forward, or moves reverse, as defined by AnimationStatus.The next section has an example of the addListener() method,and Monitoring the progress of the animation shows anexample of addStatusListener().<topic_end><topic_start>Animation examplesThis section walks you through 5 animation examples.Each section provides a link to the source code for that example.<topic_end><topic_start>Rendering animations<topic_end><topic_start>What's the point?So far you’ve learned how to generate a sequence of numbers over time.Nothing has been rendered to the screen. To render with anAnimation object, store the Animation object as amember of your widget, then use its value to decide how to draw.Consider the following app that draws the Flutter logo without animation:<code_start>import 'package:flutter/material.dart';void main() => runApp(const LogoApp());class LogoApp extends StatefulWidget {  const LogoApp({super.key});  @override  State<LogoApp> createState() => _LogoAppState();}class _LogoAppState extends State<LogoApp> {  @override  Widget build(BuildContext context) {    return Center(      child: Container(        margin: const EdgeInsets.symmetric(vertical: 10),        height: 300,        width: 300,        child: const FlutterLogo(),      ),    );  }}<code_end>App source: animate0The following shows the same code modified to animate thelogo to grow from nothing to full size.When defining an AnimationController, you must pass in avsync object. The vsync parameter is described in theAnimationController section.The changes from the non-animated example are highlighted:App source: animate1The addListener() function calls setState(),so every time the Animation generates a new number,the current frame is marked dirty, which forcesbuild() to be called again. In build(),the container changes size because its height andwidth now use animation.value instead of a hardcoded value.Dispose of the controller when the State object isdiscarded to prevent memory leaks.With these few changes,you’ve created your first animation in Flutter!Dart language tricks:  You might not be familiar with Dart’s cascade notation—the two  dots in ..addListener(). This syntax means that the addListener()  method is called with the return value from animate().  Consider the following example:This code is equivalent to:To learn more about cascades,  check out Cascade notation  in the Dart language documentation.<topic_end><topic_start>Simplifying with Animated­Widget<topic_end><topic_start>What's the point?The AnimatedWidget base class allows you to separate outthe core widget code from the animation code.AnimatedWidget doesn’t need to maintain a Stateobject to hold the animation. Add the following AnimatedLogo class:<code_start>class AnimatedLogo extends AnimatedWidget {  const AnimatedLogo({super.key, required Animation<double> animation})      : super(listenable: animation);  @override  Widget build(BuildContext context) {    final animation = listenable as Animation<double>;    return Center(      child: Container(        margin: const EdgeInsets.symmetric(vertical: 10),        height: animation.value,        width: animation.value,        child: const FlutterLogo(),      ),    );  }}<code_end>AnimatedLogo uses the current value of the animationwhen drawing itself.The LogoApp still manages the AnimationController and the Tween,and it passes the Animation object to AnimatedLogo:App source: animate2<topic_end><topic_start>Monitoring the progress of the animation<topic_end><topic_start>What's the point?It’s often helpful to know when an animation changes state,such as finishing, moving forward, or reversing.You can get notifications for this with addStatusListener().The following code modifies the previous example so thatit listens for a state change and prints an update.The highlighted line shows the change:<code_start>class _LogoAppState extends State<LogoApp> with SingleTickerProviderStateMixin {  late Animation<double> animation;  late AnimationController controller;  @override  void initState() {    super.initState();    controller =        AnimationController(duration: const Duration(seconds: 2), vsync: this);    animation = Tween<double>(begin: 0, end: 300).animate(controller)      ..addStatusListener((status) => print('$status'));    controller.forward();  }  // ...}<code_end>Running this code produces this output:Next, use addStatusListener() to reverse the animationat the beginning or the end. This creates a “breathing” effect:App source: animate3<topic_end><topic_start>Refactoring with AnimatedBuilder<topic_end><topic_start>What's the point?One problem with the code in the animate3 example,is that changing the animation required changing the widgetthat renders the logo. A better solutionis to separate responsibilities into different classes:You can accomplish this separation with the help of theAnimatedBuilder class. An AnimatedBuilder is aseparate class in the render tree. Like AnimatedWidget,AnimatedBuilder automatically listens to notificationsfrom the Animation object, and marks the widget treedirty as necessary, so you don’t need to call addListener().The widget tree for the animate4example looks like this:Starting from the bottom of the widget tree, the code for renderingthe logo is straightforward:<code_start>class LogoWidget extends StatelessWidget {  const LogoWidget({super.key});  // Leave out the height and width so it fills the animating parent  @override  Widget build(BuildContext context) {    return Container(      margin: const EdgeInsets.symmetric(vertical: 10),      child: const FlutterLogo(),    );  }}<code_end>The middle three blocks in the diagram are all created in thebuild() method in GrowTransition, shown below.The GrowTransition widget itself is stateless and holdsthe set of final variables necessary to define the transition animation.The build() function creates and returns the AnimatedBuilder,which takes the (Anonymous builder) method and theLogoWidget object as parameters. The work of rendering thetransition actually happens in the (Anonymous builder)method, which creates a Container of the appropriate sizeto force the LogoWidget to shrink to fit.One tricky point in the code below is that the child lookslike it’s specified twice. What’s happening is that theouter reference of child is passed to AnimatedBuilder,which passes it to the anonymous closure, which then usesthat object as its child. The net result is that theAnimatedBuilder is inserted in between the two widgetsin the render tree.<code_start>class GrowTransition extends StatelessWidget {  const GrowTransition(      {required this.child, required this.animation, super.key});  final Widget child;  final Animation<double> animation;  @override  Widget build(BuildContext context) {    return Center(      child: AnimatedBuilder(        animation: animation,        builder: (context, child) {          return SizedBox(            height: animation.value,            width: animation.value,            child: child,          );        },        child: child,      ),    );  }}<code_end>Finally, the code to initialize the animation looks verysimilar to the animate2 example. The initState()method creates an AnimationController and a Tween,then binds them with animate(). The magic happens inthe build() method, which returns a GrowTransitionobject with a LogoWidget as a child, and an animation object todrive the transition. These are the three elements listedin the bullet points above.App source: animate4<topic_end><topic_start>Simultaneous animations<topic_end><topic_start>What's the point?In this section, you’ll build on the example frommonitoring the progress of the animation(animate3), which used AnimatedWidgetto animate in and out continuously. Consider the casewhere you want to animate in and out while theopacity animates from transparent to opaque.info Note  This example shows how to use multiple tweens on the same animation  controller, where each tween manages a different effect in  the animation. It is for illustrative purposes only.  If you were tweening opacity and size in production code,  you’d probably use FadeTransition and SizeTransition  instead.Each tween manages an aspect of the animation. For example:<code_start>controller =    AnimationController(duration: const Duration(seconds: 2), vsync: this);sizeAnimation = Tween<double>(begin: 0, end: 300).animate(controller);opacityAnimation = Tween<double>(begin: 0.1, end: 1).animate(controller);<code_end>You can get the size with sizeAnimation.value and the opacitywith opacityAnimation.value, but the constructor for AnimatedWidgetonly takes a single Animation object. To solve this problem,the example creates its own Tween objects and explicitly calculates thevalues.Change AnimatedLogo to encapsulate its own Tween objects,and its build() method calls Tween.evaluate()on the parent’s animation object to calculatethe required size and opacity values.The following code shows the changes with highlights:<code_start>class AnimatedLogo extends AnimatedWidget {  const AnimatedLogo({super.key, required Animation<double> animation})      : super(listenable: animation);  // Make the Tweens static because they don't change.  static final _opacityTween = Tween<double>(begin: 0.1, end: 1);  static final _sizeTween = Tween<double>(begin: 0, end: 300);  @override  Widget build(BuildContext context) {    final animation = listenable as Animation<double>;    return Center(      child: Opacity(        opacity: _opacityTween.evaluate(animation),        child: Container(          margin: const EdgeInsets.symmetric(vertical: 10),          height: _sizeTween.evaluate(animation),          width: _sizeTween.evaluate(animation),          child: const FlutterLogo(),        ),      ),    );  }}class LogoApp extends StatefulWidget {  const LogoApp({super.key});  @override  State<LogoApp> createState() => _LogoAppState();}class _LogoAppState extends State<LogoApp> with SingleTickerProviderStateMixin {  late Animation<double> animation;  late AnimationController controller;  @override  void initState() {    super.initState();    controller =        AnimationController(duration: const Duration(seconds: 2), vsync: this);    animation = CurvedAnimation(parent: controller, curve: Curves.easeIn)      ..addStatusListener((status) {        if (status == AnimationStatus.completed) {          controller.reverse();        } else if (status == AnimationStatus.dismissed) {          controller.forward();        }      });    controller.forward();  }  @override  Widget build(BuildContext context) => AnimatedLogo(animation: animation);  @override  void dispose() {    controller.dispose();    super.dispose();  }}<code_end>App source: animate5<topic_end><topic_start>Next stepsThis tutorial gives you a foundation for creating animations inFlutter using Tweens, but there are many other classes to explore.You might investigate the specialized Tween classes,animations specific to Material Design,ReverseAnimation,shared element transitions (also known as Hero animations),physics simulations and fling() methods.See the animations landing pagefor the latest available documents and examples.<topic_end><topic_start>Implicit animationsWith Flutter’s animation library,you can add motion and create visual effectsfor the widgets in your UI.One part of the library is an assortment of widgetsthat manage animations for you.These widgets are collectively referred to as implicit animations,or implicitly animated widgets, deriving their name from theImplicitlyAnimatedWidget class that they implement.The following set of resources provide many ways to learnabout implicit animations in Flutter.<topic_end><topic_start>Documentation<topic_end><topic_start>Flutter in Focus videosFlutter in Focus videos feature 5-10 minute tutorialswith real code that cover techniquesthat every Flutter dev needs to know from top to bottom.The following videos cover topicsthat are relevant to implicit animations.Learn about Animation Basics with Implicit AnimationsLearn about building Custom Implicit Animations with TweenAnimationBuilder<topic_end><topic_start>The Boring ShowWatch the Boring Show to follow Google Engineers build appsfrom scratch in Flutter. The following episode coversusing implicit animations in a news aggregator app.Learn about implicitly animating the Hacker News app<topic_end><topic_start>Widget of the Week videosA weekly series of short animated videos each showingthe important features of one particular widget.In about 60 seconds, you’ll see real code for eachwidget with a demo about how it works.The following Widget of the Week videos coverimplicitly animated widgets:Learn about the AnimatedOpacity Flutter WidgetLearn about the AnimatedPadding Flutter WidgetLearn about the AnimatedPositioned Flutter WidgetLearn about the AnimatedSwitcher Flutter Widget<topic_end><topic_start>Animate the properties of a containerThe Container class provides a convenient wayto create a widget with specific properties:width, height, background color, padding, borders, and more.Simple animations often involve changing these properties over time.For example,you might want to animate the background color from grey to green toindicate that an item has been selected by the user.To animate these properties,Flutter provides the AnimatedContainer widget.Like the Container widget, AnimatedContainer allows you to definethe width, height, background colors, and more. However, when theAnimatedContainer is rebuilt with new properties, it automaticallyanimates between the old and new values. In Flutter, these types ofanimations are known as “implicit animations.”This recipe describes how to use an AnimatedContainer to animate the size,background color, and border radius when the user taps a buttonusing the following steps:<topic_end><topic_start>1. Create a StatefulWidget with default propertiesTo start, create StatefulWidget and State classes.Use the custom State class to define the properties that change overtime. In this example, that includes the width, height, color, and borderradius. You can also define the default value of each property.These properties belong to a custom State class so theycan be updated when the user taps a button.<code_start>class AnimatedContainerApp extends StatefulWidget {  const AnimatedContainerApp({super.key});  @override  State<AnimatedContainerApp> createState() => _AnimatedContainerAppState();}class _AnimatedContainerAppState extends State<AnimatedContainerApp> {  // Define the various properties with default values. Update these properties  // when the user taps a FloatingActionButton.  double _width = 50;  double _height = 50;  Color _color = Colors.green;  BorderRadiusGeometry _borderRadius = BorderRadius.circular(8);  @override  Widget build(BuildContext context) {    // Fill this out in the next steps.  }}<code_end><topic_end><topic_start>2. Build an AnimatedContainer using the propertiesNext, build the AnimatedContainer using the properties defined in theprevious step. Furthermore, provide a duration that defines how longthe animation should run.<code_start>AnimatedContainer(  // Use the properties stored in the State class.  width: _width,  height: _height,  decoration: BoxDecoration(    color: _color,    borderRadius: _borderRadius,  ),  // Define how long the animation should take.  duration: const Duration(seconds: 1),  // Provide an optional curve to make the animation feel smoother.  curve: Curves.fastOutSlowIn,)<code_end><topic_end><topic_start>3. Start the animation by rebuilding with new propertiesFinally, start the animation by rebuilding theAnimatedContainer with the new properties.How to trigger a rebuild?Use the setState() method.Add a button to the app. When the user taps the button, updatethe properties with a new width, height, background color and border radiusinside a call to setState().A real app typically transitions between fixed values (for example,from a grey to a green background). For this app,generate new values each time the user taps the button.<code_start>FloatingActionButton(  // When the user taps the button  onPressed: () {    // Use setState to rebuild the widget with new values.    setState(() {      // Create a random number generator.      final random = Random();      // Generate a random width and height.      _width = random.nextInt(300).toDouble();      _height = random.nextInt(300).toDouble();      // Generate a random color.      _color = Color.fromRGBO(        random.nextInt(256),        random.nextInt(256),        random.nextInt(256),        1,      );      // Generate a random border radius.      _borderRadius =          BorderRadius.circular(random.nextInt(100).toDouble());    });  },  child: const Icon(Icons.play_arrow),)<code_end><topic_end><topic_start>Interactive example<code_start>import 'dart:math';import 'package:flutter/material.dart';void main() => runApp(const AnimatedContainerApp());class AnimatedContainerApp extends StatefulWidget {  const AnimatedContainerApp({super.key});  @override  State<AnimatedContainerApp> createState() => _AnimatedContainerAppState();}class _AnimatedContainerAppState extends State<AnimatedContainerApp> {  // Define the various properties with default values. Update these properties  // when the user taps a FloatingActionButton.  double _width = 50;  double _height = 50;  Color _color = Colors.green;  BorderRadiusGeometry _borderRadius = BorderRadius.circular(8);  @override  Widget build(BuildContext context) {    return MaterialApp(      home: Scaffold(        appBar: AppBar(          title: const Text('AnimatedContainer Demo'),        ),        body: Center(          child: AnimatedContainer(            // Use the properties stored in the State class.            width: _width,            height: _height,            decoration: BoxDecoration(              color: _color,              borderRadius: _borderRadius,            ),            // Define how long the animation should take.            duration: const Duration(seconds: 1),            // Provide an optional curve to make the animation feel smoother.            curve: Curves.fastOutSlowIn,          ),        ),        floatingActionButton: FloatingActionButton(          // When the user taps the button          onPressed: () {            // Use setState to rebuild the widget with new values.            setState(() {              // Create a random number generator.              final random = Random();              // Generate a random width and height.              _width = random.nextInt(300).toDouble();              _height = random.nextInt(300).toDouble();              // Generate a random color.              _color = Color.fromRGBO(                random.nextInt(256),                random.nextInt(256),                random.nextInt(256),                1,              );              // Generate a random border radius.              _borderRadius =                  BorderRadius.circular(random.nextInt(100).toDouble());            });          },          child: const Icon(Icons.play_arrow),        ),      ),    );  }}<code_end><topic_end><topic_start>Fade a widget in and outUI developers often need to show and hide elements on screen.However, quickly popping elements on and off the screen canfeel jarring to end users. Instead,fade elements in and out with an opacity animation to createa smooth experience.The AnimatedOpacity widget makes it easy to perform opacityanimations. This recipe uses the following steps:<topic_end><topic_start>1. Create a box to fade in and outFirst, create something to fade in and out. For this example,draw a green box on screen.<code_start>Container(  width: 200,  height: 200,  color: Colors.green,)<code_end><topic_end><topic_start>2. Define a StatefulWidgetNow that you have a green box to animate,you need a way to know whether the box should be visible.To accomplish this, use a StatefulWidget.A StatefulWidget is a class that creates a State object.The State object holds some data about the app and provides a way toupdate that data. When updating the data,you can also ask Flutter to rebuild the UI with those changes.In this case, you have one piece of data:a boolean representing whether the button is visible.To construct a StatefulWidget, create two classes: AStatefulWidget and a corresponding State class.Pro tip: The Flutter plugins for Android Studio and VSCode includethe stful snippet to quickly generate this code.<code_start>// The StatefulWidget's job is to take data and create a State class.// In this case, the widget takes a title, and creates a _MyHomePageState.class MyHomePage extends StatefulWidget {  final String title;  const MyHomePage({    super.key,    required this.title,  });  @override  State<MyHomePage> createState() => _MyHomePageState();}// The State class is responsible for two things: holding some data you can// update and building the UI using that data.class _MyHomePageState extends State<MyHomePage> {  // Whether the green box should be visible.  bool _visible = true;  @override  Widget build(BuildContext context) {    // The green box goes here with some other Widgets.  }}<code_end><topic_end><topic_start>3. Display a button that toggles the visibilityNow that you have some data to determine whether the green boxshould be visible, you need a way to update that data.In this example, if the box is visible, hide it.If the box is hidden, show it.To handle this, display a button. When a user presses the button,flip the boolean from true to false, or false to true.Make this change using setState(),which is a method on the State class.This tells Flutter to rebuild the widget.For more information on working with user input,see the Gestures section of the cookbook.<code_start>FloatingActionButton(  onPressed: () {    // Call setState. This tells Flutter to rebuild the    // UI with the changes.    setState(() {      _visible = !_visible;    });  },  tooltip: 'Toggle Opacity',  child: const Icon(Icons.flip),)<code_end><topic_end><topic_start>4. Fade the box in and outYou have a green box on screen and a button to toggle the visibilityto true or false. How to fade the box in and out? With anAnimatedOpacity widget.The AnimatedOpacity widget requires three arguments:<code_start>AnimatedOpacity(  // If the widget is visible, animate to 0.0 (invisible).  // If the widget is hidden, animate to 1.0 (fully visible).  opacity: _visible ? 1.0 : 0.0,  duration: const Duration(milliseconds: 500),  // The green box must be a child of the AnimatedOpacity widget.  child: Container(    width: 200,    height: 200,    color: Colors.green,  ),)<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const appTitle = 'Opacity Demo';    return const MaterialApp(      title: appTitle,      home: MyHomePage(title: appTitle),    );  }}// The StatefulWidget's job is to take data and create a State class.// In this case, the widget takes a title, and creates a _MyHomePageState.class MyHomePage extends StatefulWidget {  const MyHomePage({    super.key,    required this.title,  });  final String title;  @override  State<MyHomePage> createState() => _MyHomePageState();}// The State class is responsible for two things: holding some data you can// update and building the UI using that data.class _MyHomePageState extends State<MyHomePage> {  // Whether the green box should be visible  bool _visible = true;  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text(widget.title),      ),      body: Center(        child: AnimatedOpacity(          // If the widget is visible, animate to 0.0 (invisible).          // If the widget is hidden, animate to 1.0 (fully visible).          opacity: _visible ? 1.0 : 0.0,          duration: const Duration(milliseconds: 500),          // The green box must be a child of the AnimatedOpacity widget.          child: Container(            width: 200,            height: 200,            color: Colors.green,          ),        ),      ),      floatingActionButton: FloatingActionButton(        onPressed: () {          // Call setState. This tells Flutter to rebuild the          // UI with the changes.          setState(() {            _visible = !_visible;          });        },        tooltip: 'Toggle Opacity',        child: const Icon(Icons.flip),      ),    );  }}<code_end><topic_end><topic_start>Hero animations<topic_end><topic_start>What you'll learnYou’ve probably seen hero animations many times. For example, a screen displaysa list of thumbnails representing items for sale.  Selecting an item flies it toa new screen, containing more details and a “Buy” button. Flying an image fromone screen to another is called a hero animation in Flutter, though the samemotion is sometimes referred to as a shared element transition.You might want to watch this one-minute video introducing the Hero widget:This guide demonstrates how to build standard hero animations, and heroanimations that transform the image from a circular shape to a square shapeduring flight.Examples: This guide provides examples of each hero animation style at  the following links.New to Flutter?  This page assumes you know how to create a layout  using Flutter’s widgets. For more information, see  Building Layouts in Flutter.Terminology:  A Route describes a page or screen in a Flutter app.You can create this animation in Flutter with Hero widgets.As the hero animates from the source to the destination route,the destination route (minus the hero) fades into view.Typically, heroes are small parts of the UI, like images,that both routes have in common. From the user’s perspectivethe hero “flies” between the routes. This guide shows howto create the following hero animations:Standard hero animationsA standard hero animation flies the hero from one route to a new route,usually landing at a different location and with a different size.The following video (recorded at slow speed) shows a typical example.Tapping the flippers in the center of the route flies them to theupper left corner of a new, blue route, at a smaller size.Tapping the flippers in the blue route (or using the device’sback-to-previous-route gesture) flies the flippers back tothe original route.Radial hero animationsIn radial hero animation, as the hero flies between routesits shape appears to change from circular to rectangular.The following video (recorded at slow speed),shows an example of a radial hero animation. At the start, arow of three circular images appears at the bottom of the route.Tapping any of the circular images flies that image to a new routethat displays it with a square shape.Tapping the square image flies the hero back tothe original route, displayed with a circular shape.Before moving to the sections specific tostandardor radial hero animations,read basic structure of a hero animationto learn how to structure hero animation code,and behind the scenes to understandhow Flutter performs a hero animation.<topic_end><topic_start>Basic structure of a hero animation<topic_end><topic_start>What's the point?Terminology:  If the concept of tweens or tweening is new to you,  see the Animations in Flutter tutorial.Hero animations are implemented using two Herowidgets: one describing the widget in the source route,and another describing the widget in the destination route.From the user’s point of view, the hero appears to be shared, andonly the programmer needs to understand this implementation detail.Hero animation code has the following structure:Flutter calculates the tween that animates the Hero’s bounds fromthe starting point to the endpoint (interpolating size and position),and performs the animation in an overlay.The next section describes Flutter’s process in greater detail.<topic_end><topic_start>Behind the scenesThe following describes how Flutter performs thetransition from one route to another.Before transition, the source hero waits in the sourceroute’s widget tree. The destination route does not yet exist,and the overlay is empty.Pushing a route to the Navigator triggers the animation.At t=0.0, Flutter does the following:Calculates the destination hero’s path, offscreen,using the curved motion as described in the Materialmotion spec. Flutter now knows where the hero ends up.Places the destination hero in the overlay,at the same location and size as the source hero.Adding a hero to the overlay changes its Z-order so that itappears on top of all routes.Moves the source hero offscreen.As the hero flies, its rectangular bounds are animated usingTween<Rect>, specified in Hero’screateRectTween property.By default, Flutter uses an instance ofMaterialRectArcTween, which animates therectangle’s opposing corners along a curved path.(See Radial hero animations for an examplethat uses a different Tween animation.)When the flight completes:Flutter moves the hero widget from the overlay tothe destination route. The overlay is now empty.The destination hero appears in its final positionin the destination route.The source hero is restored to its route.Popping the route performs the same process,animating the hero back to its sizeand location in the source route.<topic_end><topic_start>Essential classesThe examples in this guide use the following classes toimplement hero animations:<topic_end><topic_start>Standard hero animations<topic_end><topic_start>What's the point?Standard hero animation codeEach of the following examples demonstrates flying an image from one  route to another. This guide describes the first example.<topic_end><topic_start>What’s going on?Flying an image from one route to another is easy to implementusing Flutter’s hero widget. When using MaterialPageRouteto specify the new route, the image flies along a curved path,as described by the Material Design motion spec.Create a new Flutter example andupdate it using the files from the hero_animation.To run the example:<topic_end><topic_start>PhotoHero classThe custom PhotoHero class maintains the hero,and its size, image, and behavior when tapped.The PhotoHero builds the following widget tree:Here’s the code:Key information:<topic_end><topic_start>HeroAnimation classThe HeroAnimation class creates the source and destinationPhotoHeroes, and sets up the transition.Here’s the code:Key information:<topic_end><topic_start>Radial hero animations<topic_end><topic_start>What's the point?Flying a hero from one route to another as it transformsfrom a circular shape to a rectangular shape is a slickeffect that you can implement using Hero widgets.To accomplish this, the code animates the intersection oftwo clip shapes: a circle and a square.Throughout the animation, the circle clip (and the image)scales from minRadius to maxRadius, while the squareclip maintains constant size. At the same time,the image flies from its position in the source route to itsposition in the destination route. For visual examplesof this transition, see Radial transformationin the Material motion spec.This animation might seem complex (and it is), but you can customize theprovided example to your needs. The heavy lifting is done for you.Radial hero animation codeEach of the following examples demonstrates a radial hero animation.  This guide describes the first example.Pro tip:  The radial hero animation involves intersecting a round shape with  a square shape. This can be hard to see, even when slowing  the animation with timeDilation, so you might consider enabling  the debugPaintSizeEnabled flag during development.<topic_end><topic_start>What’s going on?The following diagram shows the clipped image at the beginning(t = 0.0), and the end (t = 1.0) of the animation.The blue gradient (representing the image), indicates where the clipshapes intersect. At the beginning of the transition,the result of the intersection is a circular clip (ClipOval).During the transformation, the ClipOval scales from minRadiusto maxRadius while the ClipRect maintains a constant size.At the end of the transition the intersection of the circular andrectangular clips yield a rectangle that’s the same size as the herowidget. In other words, at the end of the transition the image is nolonger clipped.Create a new Flutter example andupdate it using the files from theradial_hero_animation GitHub directory.To run the example:<topic_end><topic_start>Photo classThe Photo class builds the widget tree that holds the image:Key information:<topic_end><topic_start>RadialExpansion classThe RadialExpansion widget, the core of the demo, builds thewidget tree that clips the image during the transition.The clipped shape results from the intersection of a circular clip(that grows during flight),with a rectangular clip (that remains a constant size throughout).To do this, it builds the following widget tree:Here’s the code:Key information:The example defines the tweening interpolation usingMaterialRectCenterArcTween.The default flight path for a hero animationinterpolates the tweens using the corners of the heroes.This approach affects the hero’s aspect ratio duringthe radial transformation, so the new flight path usesMaterialRectCenterArcTween to interpolate the tweens using thecenter point of each hero.Here’s the code:The hero’s flight path still follows an arc,but the image’s aspect ratio remains constant.<topic_end><topic_start>Animate a page route transitionA design language, such as Material, defines standard behaviors whentransitioning between routes (or screens). Sometimes, though, a customtransition between screens can make an app more unique. To help,PageRouteBuilder provides an Animation object.This Animation can be used with Tween andCurve objects to customize the transition animation.This recipe shows how to transition betweenroutes by animating the new route into view fromthe bottom of the screen.To create a custom page route transition, this recipe uses the following steps:<topic_end><topic_start>1. Set up a PageRouteBuilderTo start, use a PageRouteBuilder to create a Route.PageRouteBuilder has two callbacks, one to build the content of the route(pageBuilder), and one to build the route’s transition (transitionsBuilder).info Note  The child parameter in transitionsBuilder is the widget returned from  pageBuilder. The pageBuilder function is only called the first time the  route is built. The framework can avoid extra work because child stays the  same throughout the transition.The following example creates two routes: a home route with a “Go!” button, anda second route titled “Page 2”.<code_start>import 'package:flutter/material.dart';void main() {  runApp(    const MaterialApp(      home: Page1(),    ),  );}class Page1 extends StatelessWidget {  const Page1({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(),      body: Center(        child: ElevatedButton(          onPressed: () {            Navigator.of(context).push(_createRoute());          },          child: const Text('Go!'),        ),      ),    );  }}Route _createRoute() {  return PageRouteBuilder(    pageBuilder: (context, animation, secondaryAnimation) => const Page2(),    transitionsBuilder: (context, animation, secondaryAnimation, child) {      return child;    },  );}class Page2 extends StatelessWidget {  const Page2({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(),      body: const Center(        child: Text('Page 2'),      ),    );  }}<code_end><topic_end><topic_start>2. Create a TweenTo make the new page animate in from the bottom, it should animate fromOffset(0,1) to Offset(0, 0) (usually defined using the Offset.zeroconstructor). In this case, the Offset is a 2D vector for the‘FractionalTranslation’ widget.Setting the dy argument to 1 represents a vertical translation onefull height of the page.The transitionsBuilder callback has an animation parameter. It’s anAnimation<double> that produces values between 0 and 1. Convert theAnimation into an Animation using a Tween:<code_start>transitionsBuilder: (context, animation, secondaryAnimation, child) {  const begin = Offset(0.0, 1.0);  const end = Offset.zero;  final tween = Tween(begin: begin, end: end);  final offsetAnimation = animation.drive(tween);  return child;},<code_end><topic_end><topic_start>3. Use an AnimatedWidgetFlutter has a set of widgets extending AnimatedWidgetthat rebuild themselves when the value of the animation changes. For instance,SlideTransition takes an Animation<Offset> and translates its child (using aFractionalTranslation widget) whenever the value of the animation changes.AnimatedWidget Return a SlideTransitionwith the Animation<Offset> and the child widget:<code_start>transitionsBuilder: (context, animation, secondaryAnimation, child) {  const begin = Offset(0.0, 1.0);  const end = Offset.zero;  final tween = Tween(begin: begin, end: end);  final offsetAnimation = animation.drive(tween);  return SlideTransition(    position: offsetAnimation,    child: child,  );},<code_end><topic_end><topic_start>4. Use a CurveTweenFlutter provides a selection of easing curves thatadjust the rate of the animation over time.The Curves classprovides a predefined set of commonly used curves.For example, Curves.easeOutmakes the animation start quickly and end slowly.To use a Curve, create a new CurveTweenand pass it a Curve:<code_start>var curve = Curves.ease;var curveTween = CurveTween(curve: curve);<code_end>This new Tween still produces values from 0 to 1. In the next step, it will becombined the Tween<Offset> from step 2.<topic_end><topic_start>5. Combine the two TweensTo combine the tweens,use chain():<code_start>const begin = Offset(0.0, 1.0);const end = Offset.zero;const curve = Curves.ease;var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));<code_end>Then use this tween by passing it to animation.drive(). This creates a newAnimation<Offset> that can be given to the SlideTransition widget:<code_start>return SlideTransition(  position: animation.drive(tween),  child: child,);<code_end>This new Tween (or Animatable) produces Offset values by first evaluating theCurveTween, then evaluating the Tween<Offset>. When the animation runs, thevalues are computed in this order:Another way to create an Animation<Offset> with an easing curve is to use aCurvedAnimation:<code_start>transitionsBuilder: (context, animation, secondaryAnimation, child) {  const begin = Offset(0.0, 1.0);  const end = Offset.zero;  const curve = Curves.ease;  final tween = Tween(begin: begin, end: end);  final curvedAnimation = CurvedAnimation(    parent: animation,    curve: curve,  );  return SlideTransition(    position: tween.animate(curvedAnimation),    child: child,  );}<code_end><topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() {  runApp(    const MaterialApp(      home: Page1(),    ),  );}class Page1 extends StatelessWidget {  const Page1({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(),      body: Center(        child: ElevatedButton(          onPressed: () {            Navigator.of(context).push(_createRoute());          },          child: const Text('Go!'),        ),      ),    );  }}Route _createRoute() {  return PageRouteBuilder(    pageBuilder: (context, animation, secondaryAnimation) => const Page2(),    transitionsBuilder: (context, animation, secondaryAnimation, child) {      const begin = Offset(0.0, 1.0);      const end = Offset.zero;      const curve = Curves.ease;      var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));      return SlideTransition(        position: animation.drive(tween),        child: child,      );    },  );}class Page2 extends StatelessWidget {  const Page2({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(),      body: const Center(        child: Text('Page 2'),      ),    );  }}<code_end><topic_end><topic_start>Animate a widget using a physics simulationPhysics simulations can make app interactions feel realistic and interactive.For example, you might want to animate a widget to act as if it were attached toa spring or falling with gravity.This recipe demonstrates how to move a widget from a dragged point back to thecenter using a spring simulation.This recipe uses these steps:<topic_end><topic_start>Step 1: Set up an animation controllerStart with a stateful widget called DraggableCard:<code_start>import 'package:flutter/material.dart';void main() {  runApp(const MaterialApp(home: PhysicsCardDragDemo()));}class PhysicsCardDragDemo extends StatelessWidget {  const PhysicsCardDragDemo({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(),      body: const DraggableCard(        child: FlutterLogo(          size: 128,        ),      ),    );  }}class DraggableCard extends StatefulWidget {  const DraggableCard({required this.child, super.key});  final Widget child;  @override  State<DraggableCard> createState() => _DraggableCardState();}class _DraggableCardState extends State<DraggableCard> {  @override  void initState() {    super.initState();  }  @override  void dispose() {    super.dispose();  }  @override  Widget build(BuildContext context) {    return Align(      child: Card(        child: widget.child,      ),    );  }}<code_end>Make the _DraggableCardState class extend fromSingleTickerProviderStateMixin.Then construct an AnimationController ininitState and set vsync to this.info Note  Extending SingleTickerProviderStateMixin allows the state object to be a  TickerProvider for the AnimationController. For more information, see the  documentation for TickerProvider.<topic_end><topic_start>Step 2: Move the widget using gesturesMake the widget move when it’s dragged, and add an Alignment field to the_DraggableCardState class:Add a GestureDetector that handles the onPanDown, onPanUpdate, andonPanEnd callbacks. To adjust the alignment, use a MediaQuery to get thesize of the widget, and divide by 2. (This converts units of “pixels dragged” tocoordinates that Align uses.) Then, set the Align widget’s alignment to_dragAlignment:<topic_end><topic_start>Step 3: Animate the widgetWhen the widget is released, it should spring back to the center.Add an Animation<Alignment> field and an _runAnimation method. Thismethod defines a Tween that interpolates between the point the widget wasdragged to, to the point in the center.<code_start>void _runAnimation() {  _animation = _controller.drive(    AlignmentTween(      begin: _dragAlignment,      end: Alignment.center,    ),  );  _controller.reset();  _controller.forward();}<code_end>Next, update _dragAlignment when the AnimationController produces avalue:Next, make the Align widget use the _dragAlignment field:<code_start>child: Align(  alignment: _dragAlignment,  child: Card(    child: widget.child,  ),),<code_end>Finally, update the GestureDetector to manage the animation controller:<topic_end><topic_start>Step 4: Calculate the velocity to simulate a springing motionThe last step is to do a little math, to calculate the velocity of the widgetafter it’s finished being dragged. This is so that the widget realisticallycontinues at that speed before being snapped back. (The _runAnimation methodalready sets the direction by setting the animation’s start and end alignment.)First, import the physics package:<code_start>import 'package:flutter/physics.dart';<code_end>The onPanEnd callback provides a DragEndDetails object. This objectprovides the velocity of the pointer when it stopped contacting the screen. Thevelocity is in pixels per second, but the Align widget doesn’t use pixels. Ituses coordinate values between [-1.0, -1.0] and [1.0, 1.0], where [0.0, 0.0]represents the center. The size calculated in step 2 is used to convert pixelsto coordinate values in this range.Finally, AnimationController has an animateWith() method that can be given aSpringSimulation:<code_start>/// Calculates and runs a [SpringSimulation].void _runAnimation(Offset pixelsPerSecond, Size size) {  _animation = _controller.drive(    AlignmentTween(      begin: _dragAlignment,      end: Alignment.center,    ),  );  // Calculate the velocity relative to the unit interval, [0,1],  // used by the animation controller.  final unitsPerSecondX = pixelsPerSecond.dx / size.width;  final unitsPerSecondY = pixelsPerSecond.dy / size.height;  final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY);  final unitVelocity = unitsPerSecond.distance;  const spring = SpringDescription(    mass: 30,    stiffness: 1,    damping: 1,  );  final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);  _controller.animateWith(simulation);}<code_end>Don’t forget to call _runAnimation()  with the velocity and size:<code_start>onPanEnd: (details) {  _runAnimation(details.velocity.pixelsPerSecond, size);},<code_end>info Note  Now that the animation controller uses a simulation it’s duration argument  is no longer required.<topic_end><topic_start>Interactive Example<code_start>import 'package:flutter/material.dart';import 'package:flutter/physics.dart';void main() {  runApp(const MaterialApp(home: PhysicsCardDragDemo()));}class PhysicsCardDragDemo extends StatelessWidget {  const PhysicsCardDragDemo({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(),      body: const DraggableCard(        child: FlutterLogo(          size: 128,        ),      ),    );  }}/// A draggable card that moves back to [Alignment.center] when it's/// released.class DraggableCard extends StatefulWidget {  const DraggableCard({required this.child, super.key});  final Widget child;  @override  State<DraggableCard> createState() => _DraggableCardState();}class _DraggableCardState extends State<DraggableCard>    with SingleTickerProviderStateMixin {  late AnimationController _controller;  /// The alignment of the card as it is dragged or being animated.  ///  /// While the card is being dragged, this value is set to the values computed  /// in the GestureDetector onPanUpdate callback. If the animation is running,  /// this value is set to the value of the [_animation].  Alignment _dragAlignment = Alignment.center;  late Animation<Alignment> _animation;  /// Calculates and runs a [SpringSimulation].  void _runAnimation(Offset pixelsPerSecond, Size size) {    _animation = _controller.drive(      AlignmentTween(        begin: _dragAlignment,        end: Alignment.center,      ),    );    // Calculate the velocity relative to the unit interval, [0,1],    // used by the animation controller.    final unitsPerSecondX = pixelsPerSecond.dx / size.width;    final unitsPerSecondY = pixelsPerSecond.dy / size.height;    final unitsPerSecond = Offset(unitsPerSecondX, unitsPerSecondY);    final unitVelocity = unitsPerSecond.distance;    const spring = SpringDescription(      mass: 30,      stiffness: 1,      damping: 1,    );    final simulation = SpringSimulation(spring, 0, 1, -unitVelocity);    _controller.animateWith(simulation);  }  @override  void initState() {    super.initState();    _controller = AnimationController(vsync: this);    _controller.addListener(() {      setState(() {        _dragAlignment = _animation.value;      });    });  }  @override  void dispose() {    _controller.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    final size = MediaQuery.of(context).size;    return GestureDetector(      onPanDown: (details) {        _controller.stop();      },      onPanUpdate: (details) {        setState(() {          _dragAlignment += Alignment(            details.delta.dx / (size.width / 2),            details.delta.dy / (size.height / 2),          );        });      },      onPanEnd: (details) {        _runAnimation(details.velocity.pixelsPerSecond, size);      },      child: Align(        alignment: _dragAlignment,        child: Card(          child: widget.child,        ),      ),    );  }}<code_end><topic_end><topic_start>Staggered animations<topic_end><topic_start>What you'll learnTerminology:  If the concept of tweens or tweening is new to you, see the  Animations in Flutter tutorial.Staggered animations are a straightforward concept: visual changeshappen as a series of operations, rather than all at once.The animation might be purely sequential, with one change occurring afterthe next, or it might partially or completely overlap. It might alsohave gaps, where no changes occur.This guide shows how to build a staggered animation in Flutter.<topic_end><topic_start>ExamplesThis guide explains the basic_staggered_animation example.  You can also refer to a more complex example,  staggered_pic_selection.The following video demonstrates the animation performed bybasic_staggered_animation:In the video, you see the following animation of a single widget,which begins as a bordered blue square with slightly rounded corners.The square runs through changes in the following order:After running forward, the animation runs in reverse.New to Flutter?  This page assumes you know how to create a layout using Flutter’s  widgets.  For more information, see Building Layouts in Flutter.<topic_end><topic_start>Basic structure of a staggered animation<topic_end><topic_start>What's the point?The following diagram shows the Intervals used in thebasic_staggered_animation example.You might notice the following characteristics:To set up the animation:When the controlling animation’s value changes,the new animation’s value changes, triggering the UI to update.The following code creates a tween for the width property.It builds a CurvedAnimation,specifying an eased curve. See Curves forother available pre-defined animation curves.The begin and end values don’t have to be doubles.The following code builds the tween for the borderRadius property(which controls the roundness of the square’s corners),using BorderRadius.circular().<topic_end><topic_start>Complete staggered animationLike all interactive widgets, the complete animation consistsof a widget pair: a stateless and a stateful widget.The stateless widget specifies the Tweens,defines the Animation objects, and provides a build() functionresponsible for building the animating portion of the widget tree.The stateful widget creates the controller, plays the animation,and builds the non-animating portion of the widget tree.The animation begins when a tap is detected anywhere in the screen.Full code for basic_staggered_animation’s main.dart<topic_end><topic_start>Stateless widget: StaggerAnimationIn the stateless widget, StaggerAnimation,the build() function instantiates anAnimatedBuilder—a general purpose widget for buildinganimations. The AnimatedBuilderbuilds a widget and configures it using the Tweens’ current values.The example creates a function named _buildAnimation() (which performsthe actual UI updates), and assigns it to its builder property.AnimatedBuilder listens to notifications from the animation controller,marking the widget tree dirty as values change.For each tick of the animation, the values are updated,resulting in a call to _buildAnimation().<topic_end><topic_start>Stateful widget: StaggerDemoThe stateful widget, StaggerDemo, creates the AnimationController(the one who rules them all), specifying a 2000 ms duration. It playsthe animation, and builds the non-animating portion of the widget tree.The animation begins when a tap is detected in the screen.The animation runs forward, then backward.<topic_end><topic_start>Create a staggered menu animationA single app screen might contain multiple animations.Playing all of the animations at the same time can beoverwhelming. Playing the animations one after the othercan take too long. A better option is to stagger the animations. Each animation begins at a different time,but the animations overlap to create a shorter duration.In this recipe, you build a drawer menu with animated content that is staggered and has a button that popsin at the bottom.The following animation shows the app’s behavior:<topic_end><topic_start>Create the menu without animationsThe drawer menu displays a list of titles,followed by a Get started button at the bottom of the menu.Define a stateful widget called Menuthat displays the list and button in static locations.<code_start>class Menu extends StatefulWidget {  const Menu({super.key});  @override  State<Menu> createState() => _MenuState();}class _MenuState extends State<Menu> {  static const _menuTitles = [    'Declarative Style',    'Premade Widgets',    'Stateful Hot Reload',    'Native Performance',    'Great Community',  ];  @override  Widget build(BuildContext context) {    return Container(      color: Colors.white,      child: Stack(        fit: StackFit.expand,        children: [          _buildFlutterLogo(),          _buildContent(),        ],      ),    );  }  Widget _buildFlutterLogo() {    // TODO: We'll implement this later.    return Container();  }  Widget _buildContent() {    return Column(      crossAxisAlignment: CrossAxisAlignment.start,      children: [        const SizedBox(height: 16),        ..._buildListItems(),        const Spacer(),        _buildGetStartedButton(),      ],    );  }  List<Widget> _buildListItems() {    final listItems = <Widget>[];    for (var i = 0; i < _menuTitles.length; ++i) {      listItems.add(        Padding(          padding: const EdgeInsets.symmetric(horizontal: 36, vertical: 16),          child: Text(            _menuTitles[i],            textAlign: TextAlign.left,            style: const TextStyle(              fontSize: 24,              fontWeight: FontWeight.w500,            ),          ),        ),      );    }    return listItems;  }  Widget _buildGetStartedButton() {    return SizedBox(      width: double.infinity,      child: Padding(        padding: const EdgeInsets.all(24),        child: ElevatedButton(          style: ElevatedButton.styleFrom(            shape: const StadiumBorder(),            backgroundColor: Colors.blue,            padding: const EdgeInsets.symmetric(horizontal: 48, vertical: 14),          ),          onPressed: () {},          child: const Text(            'Get Started',            style: TextStyle(              color: Colors.white,              fontSize: 22,            ),          ),        ),      ),    );  }}<code_end><topic_end><topic_start>Prepare for animationsControl of the animation timing requires anAnimationController.Add the SingleTickerProviderStateMixinto the MenuState class. Then, declare andinstantiate an AnimationController.<code_start>class _MenuState extends State<Menu> with SingleTickerProviderStateMixin {  late AnimationController _staggeredController;  @override  void initState() {    super.initState();    _staggeredController = AnimationController(      vsync: this,    );  }}  @override  void dispose() {    _staggeredController.dispose();    super.dispose();  }}<code_end>The length of the delay before every animation isup to you. Define the animation delays,individual animation durations, and the total animation duration.<code_start>class _MenuState extends State<Menu> with SingleTickerProviderStateMixin {  static const _initialDelayTime = Duration(milliseconds: 50);  static const _itemSlideTime = Duration(milliseconds: 250);  static const _staggerTime = Duration(milliseconds: 50);  static const _buttonDelayTime = Duration(milliseconds: 150);  static const _buttonTime = Duration(milliseconds: 500);  final _animationDuration = _initialDelayTime +      (_staggerTime * _menuTitles.length) +      _buttonDelayTime +      _buttonTime;}<code_end>In this case, all the animations are delayed by 50 ms.After that, list items begin to appear.Each list item’s appearance is delayed by 50 ms after the previous list item begins to slide in.Each list item takes 250 ms to slide from right to left.After the last list item begins to slide in,the button at the bottom waits another 150 ms to pop in.The button animation takes 500 ms.With each delay and animation duration defined,the total duration is calculated so that it can beused to calculate the individual animation times.The desired animation times are shown in the following diagram:To animate a value during a subsection of a larger animation,Flutter provides the Interval class.An Interval takes a start time percentage and an end time percentage. That Interval can then be used toanimate a value between those start and end times,instead of using the entire animation’s start and end times. For example, given an animation that takes 1 second, an interval from 0.2 to 0.5 would start at 200 ms(20%) and end at 500 ms (50%).Declare and calculate each list item’s Interval and the bottom button Interval.<code_start>class _MenuState extends State<Menu> with SingleTickerProviderStateMixin {  final List<Interval> _itemSlideIntervals = [];  late Interval _buttonInterval;  @override  void initState() {    super.initState();    _createAnimationIntervals();    _staggeredController = AnimationController(      vsync: this,      duration: _animationDuration,    );  }  void _createAnimationIntervals() {    for (var i = 0; i < _menuTitles.length; ++i) {      final startTime = _initialDelayTime + (_staggerTime * i);      final endTime = startTime + _itemSlideTime;      _itemSlideIntervals.add(        Interval(          startTime.inMilliseconds / _animationDuration.inMilliseconds,          endTime.inMilliseconds / _animationDuration.inMilliseconds,        ),      );    }    final buttonStartTime =        Duration(milliseconds: (_menuTitles.length * 50)) + _buttonDelayTime;    final buttonEndTime = buttonStartTime + _buttonTime;    _buttonInterval = Interval(      buttonStartTime.inMilliseconds / _animationDuration.inMilliseconds,      buttonEndTime.inMilliseconds / _animationDuration.inMilliseconds,    );  }}<code_end><topic_end><topic_start>Animate the list items and buttonThe staggered animation plays as soon as the menu becomes visible.Start the animation in initState().<code_start>@overridevoid initState() {  super.initState();  _createAnimationIntervals();  _staggeredController = AnimationController(    vsync: this,    duration: _animationDuration,  )..forward();}<code_end>Each list item slides from right to left andfades in at the same time.Use the list item’s Interval and an easeOutcurve to animate the opacity and translationvalues for each list item.<code_start>List<Widget> _buildListItems() {  final listItems = <Widget>[];  for (var i = 0; i < _menuTitles.length; ++i) {    listItems.add(      AnimatedBuilder(        animation: _staggeredController,        builder: (context, child) {          final animationPercent = Curves.easeOut.transform(            _itemSlideIntervals[i].transform(_staggeredController.value),          );          final opacity = animationPercent;          final slideDistance = (1.0 - animationPercent) * 150;          return Opacity(            opacity: opacity,            child: Transform.translate(              offset: Offset(slideDistance, 0),              child: child,            ),          );        },        child: Padding(          padding: const EdgeInsets.symmetric(horizontal: 36, vertical: 16),          child: Text(            _menuTitles[i],            textAlign: TextAlign.left,            style: const TextStyle(              fontSize: 24,              fontWeight: FontWeight.w500,            ),          ),        ),      ),    );  }  return listItems;}<code_end>Use the same approach to animate the opacity andscale of the bottom button. This time, use anelasticOut curve to give the button a springy effect.<code_start>Widget _buildGetStartedButton() {  return SizedBox(    width: double.infinity,    child: Padding(      padding: const EdgeInsets.all(24),      child: AnimatedBuilder(        animation: _staggeredController,        builder: (context, child) {          final animationPercent = Curves.elasticOut.transform(              _buttonInterval.transform(_staggeredController.value));          final opacity = animationPercent.clamp(0.0, 1.0);          final scale = (animationPercent * 0.5) + 0.5;          return Opacity(            opacity: opacity,            child: Transform.scale(              scale: scale,              child: child,            ),          );        },        child: ElevatedButton(          style: ElevatedButton.styleFrom(            shape: const StadiumBorder(),            backgroundColor: Colors.blue,            padding: const EdgeInsets.symmetric(horizontal: 48, vertical: 14),          ),          onPressed: () {},          child: const Text(            'Get Started',            style: TextStyle(              color: Colors.white,              fontSize: 22,            ),          ),        ),      ),    ),  );}<code_end>Congratulations!You have an animated menu where the appearance of each list item is staggered, followed by a bottom button thatpops into place.<topic_end><topic_start>Interactive example<code_start>import 'package:flutter/material.dart';void main() {  runApp(    const MaterialApp(      home: ExampleStaggeredAnimations(),      debugShowCheckedModeBanner: false,    ),  );}class ExampleStaggeredAnimations extends StatefulWidget {  const ExampleStaggeredAnimations({    super.key,  });  @override  State<ExampleStaggeredAnimations> createState() =>      _ExampleStaggeredAnimationsState();}class _ExampleStaggeredAnimationsState extends State<ExampleStaggeredAnimations>    with SingleTickerProviderStateMixin {  late AnimationController _drawerSlideController;  @override  void initState() {    super.initState();    _drawerSlideController = AnimationController(      vsync: this,      duration: const Duration(milliseconds: 150),    );  }  @override  void dispose() {    _drawerSlideController.dispose();    super.dispose();  }  bool _isDrawerOpen() {    return _drawerSlideController.value == 1.0;  }  bool _isDrawerOpening() {    return _drawerSlideController.status == AnimationStatus.forward;  }  bool _isDrawerClosed() {    return _drawerSlideController.value == 0.0;  }  void _toggleDrawer() {    if (_isDrawerOpen() || _isDrawerOpening()) {      _drawerSlideController.reverse();    } else {      _drawerSlideController.forward();    }  }  @override  Widget build(BuildContext context) {    return Scaffold(      backgroundColor: Colors.white,      appBar: _buildAppBar(),      body: Stack(        children: [          _buildContent(),          _buildDrawer(),        ],      ),    );  }  PreferredSizeWidget _buildAppBar() {    return AppBar(      title: const Text(        'Flutter Menu',        style: TextStyle(          color: Colors.black,        ),      ),      backgroundColor: Colors.transparent,      elevation: 0.0,      automaticallyImplyLeading: false,      actions: [        AnimatedBuilder(          animation: _drawerSlideController,          builder: (context, child) {            return IconButton(              onPressed: _toggleDrawer,              icon: _isDrawerOpen() || _isDrawerOpening()                  ? const Icon(                      Icons.clear,                      color: Colors.black,                    )                  : const Icon(                      Icons.menu,                      color: Colors.black,                    ),            );          },        ),      ],    );  }  Widget _buildContent() {    // Put page content here.    return const SizedBox();  }  Widget _buildDrawer() {    return AnimatedBuilder(      animation: _drawerSlideController,      builder: (context, child) {        return FractionalTranslation(          translation: Offset(1.0 - _drawerSlideController.value, 0.0),          child: _isDrawerClosed() ? const SizedBox() : const Menu(),        );      },    );  }}class Menu extends StatefulWidget {  const Menu({super.key});  @override  State<Menu> createState() => _MenuState();}class _MenuState extends State<Menu> with SingleTickerProviderStateMixin {  static const _menuTitles = [    'Declarative style',    'Premade widgets',    'Stateful hot reload',    'Native performance',    'Great community',  ];  static const _initialDelayTime = Duration(milliseconds: 50);  static const _itemSlideTime = Duration(milliseconds: 250);  static const _staggerTime = Duration(milliseconds: 50);  static const _buttonDelayTime = Duration(milliseconds: 150);  static const _buttonTime = Duration(milliseconds: 500);  final _animationDuration = _initialDelayTime +      (_staggerTime * _menuTitles.length) +      _buttonDelayTime +      _buttonTime;  late AnimationController _staggeredController;  final List<Interval> _itemSlideIntervals = [];  late Interval _buttonInterval;  @override  void initState() {    super.initState();    _createAnimationIntervals();    _staggeredController = AnimationController(      vsync: this,      duration: _animationDuration,    )..forward();  }  void _createAnimationIntervals() {    for (var i = 0; i < _menuTitles.length; ++i) {      final startTime = _initialDelayTime + (_staggerTime * i);      final endTime = startTime + _itemSlideTime;      _itemSlideIntervals.add(        Interval(          startTime.inMilliseconds / _animationDuration.inMilliseconds,          endTime.inMilliseconds / _animationDuration.inMilliseconds,        ),      );    }    final buttonStartTime =        Duration(milliseconds: (_menuTitles.length * 50)) + _buttonDelayTime;    final buttonEndTime = buttonStartTime + _buttonTime;    _buttonInterval = Interval(      buttonStartTime.inMilliseconds / _animationDuration.inMilliseconds,      buttonEndTime.inMilliseconds / _animationDuration.inMilliseconds,    );  }  @override  void dispose() {    _staggeredController.dispose();    super.dispose();  }  @override  Widget build(BuildContext context) {    return Container(      color: Colors.white,      child: Stack(        fit: StackFit.expand,        children: [          _buildFlutterLogo(),          _buildContent(),        ],      ),    );  }  Widget _buildFlutterLogo() {    return const Positioned(      right: -100,      bottom: -30,      child: Opacity(        opacity: 0.2,        child: FlutterLogo(          size: 400,        ),      ),    );  }  Widget _buildContent() {    return Column(      crossAxisAlignment: CrossAxisAlignment.start,      children: [        const SizedBox(height: 16),        ..._buildListItems(),        const Spacer(),        _buildGetStartedButton(),      ],    );  }  List<Widget> _buildListItems() {    final listItems = <Widget>[];    for (var i = 0; i < _menuTitles.length; ++i) {      listItems.add(        AnimatedBuilder(          animation: _staggeredController,          builder: (context, child) {            final animationPercent = Curves.easeOut.transform(              _itemSlideIntervals[i].transform(_staggeredController.value),            );            final opacity = animationPercent;            final slideDistance = (1.0 - animationPercent) * 150;            return Opacity(              opacity: opacity,              child: Transform.translate(                offset: Offset(slideDistance, 0),                child: child,              ),            );          },          child: Padding(            padding: const EdgeInsets.symmetric(horizontal: 36, vertical: 16),            child: Text(              _menuTitles[i],              textAlign: TextAlign.left,              style: const TextStyle(                fontSize: 24,                fontWeight: FontWeight.w500,              ),            ),          ),        ),      );    }    return listItems;  }  Widget _buildGetStartedButton() {    return SizedBox(      width: double.infinity,      child: Padding(        padding: const EdgeInsets.all(24),        child: AnimatedBuilder(          animation: _staggeredController,          builder: (context, child) {            final animationPercent = Curves.elasticOut.transform(                _buttonInterval.transform(_staggeredController.value));            final opacity = animationPercent.clamp(0.0, 1.0);            final scale = (animationPercent * 0.5) + 0.5;            return Opacity(              opacity: opacity,              child: Transform.scale(                scale: scale,                child: child,              ),            );          },          child: ElevatedButton(            style: ElevatedButton.styleFrom(              shape: const StadiumBorder(),              backgroundColor: Colors.blue,              padding: const EdgeInsets.symmetric(horizontal: 48, vertical: 14),            ),            onPressed: () {},            child: const Text(              'Get started',              style: TextStyle(                color: Colors.white,                fontSize: 22,              ),            ),          ),        ),      ),    );  }}<code_end><topic_end><topic_start>Animations API overviewThe animation system in Flutter is based on typedAnimation objects. Widgets can eitherincorporate these animations in their buildfunctions directly by reading their current value and listening to theirstate changes or they can use the animations as the basis of more elaborateanimations that they pass along to other widgets.<topic_end><topic_start>AnimationThe primary building block of the animation system is theAnimation class. An animation represents a valueof a specific type that can change over the lifetime ofthe animation. Most widgets that perform an animationreceive an Animation object as a parameter,from which they read the current value of the animationand to which they listen for changes to that value.<topic_end><topic_start>addListenerWhenever the animation’s value changes,the animation notifies all the listeners added withaddListener. Typically, a Stateobject that listens to an animation callssetState on itself in its listener callbackto notify the widget system that it needs torebuild with the new value of the animation.This pattern is so common that there are two widgetsthat help widgets rebuild when animations change value:AnimatedWidget and AnimatedBuilder.The first, AnimatedWidget, is most useful forstateless animated widgets. To use AnimatedWidget,simply subclass it and implement the build function.The second, AnimatedBuilder, is useful for more complex widgetsthat wish to include an animation as part of a larger build function.To use AnimatedBuilder, simply construct the widgetand pass it a builder function.<topic_end><topic_start>addStatusListenerAnimations also provide an AnimationStatus,which indicates how the animation will evolve over time.Whenever the animation’s status changes,the animation notifies all the listeners added withaddStatusListener. Typically, animations startout in the dismissed status, which means they’reat the beginning of their range. For example,animations that progress from 0.0 to 1.0will be dismissed when their value is 0.0.An animation might then run forward (from 0.0 to 1.0)or perhaps in reverse (from 1.0 to 0.0).Eventually, if the animation reaches the end of its range(1.0), the animation reaches the completed status.<topic_end><topic_start>Animation­ControllerTo create an animation, first create an AnimationController.As well as being an animation itself, an AnimationControllerlets you control the animation. For example,you can tell the controller to play the animationforward or stop the animation.You can also fling animations,which uses a physical simulation, such as a spring,to drive the animation.Once you’ve created an animation controller,you can start building other animations based on it.For example, you can create a ReverseAnimationthat mirrors the original animation but runs in theopposite direction (from 1.0 to 0.0).Similarly, you can create a CurvedAnimationwhose value is adjusted by a Curve.<topic_end><topic_start>TweensTo animate beyond the 0.0 to 1.0 interval, you can use aTween<T>, which interpolates between itsbegin and end values. Many types have specificTween subclasses that provide type-specific interpolation.For example, ColorTween interpolates between colors andRectTween interpolates between rects.You can define your own interpolations by creatingyour own subclass of Tween and overriding itslerp function.By itself, a tween just defines how to interpolatebetween two values. To get a concrete value for thecurrent frame of an animation, you also need ananimation to determine the current state.There are two ways to combine a tweenwith an animation to get a concrete value:You can evaluate the tween at the currentvalue of an animation. This approach is most usefulfor widgets that are already listening to the animation and hencerebuilding whenever the animation changes value.You can animate the tween based on the animation.Rather than returning a single value, the animate functionreturns a new Animation that incorporates the tween.This approach is most useful when you want to give thenewly created animation to another widget,which can then read the current value that incorporatesthe tween as well as listen for changes to the value.<topic_end><topic_start>ArchitectureAnimations are actually built from a number of core building blocks.<topic_end><topic_start>SchedulerThe SchedulerBinding is a singleton classthat exposes the Flutter scheduling primitives.For this discussion, the key primitive is the frame callbacks.Each time a frame needs to be shown on the screen,Flutter’s engine triggers a “begin frame” callback thatthe scheduler multiplexes to all the listeners registered usingscheduleFrameCallback(). All these callbacks aregiven the official time stamp of the frame, inthe form of a Duration from some arbitrary epoch. Since all thecallbacks have the same time, any animations triggered from thesecallbacks will appear to be exactly synchronised evenif they take a few milliseconds to be executed.<topic_end><topic_start>TickersThe Ticker class hooks into the scheduler’sscheduleFrameCallback()mechanism to invoke a callback every tick.A Ticker can be started and stopped. When started,it returns a Future that will resolve when it is stopped.Each tick, the Ticker provides the callback with theduration since the first tick after it was started.Because tickers always give their elapsed time relative to the firsttick after they were started; tickers are all synchronised. If youstart three tickers at different times between two ticks, they will allnonetheless be synchronised with the same starting time, and willsubsequently tick in lockstep. Like people at a bus-stop,all the tickers wait for a regularly occurring event(the tick) to begin moving (counting time).<topic_end><topic_start>SimulationsThe Simulation abstract class maps arelative time value (an elapsed time) to adouble value, and has a notion of completion.In principle simulations are stateless but in practicesome simulations (for example,BouncingScrollSimulation andClampingScrollSimulation)change state irreversibly when queried.There are various concrete implementationsof the Simulation class for different effects.<topic_end><topic_start>AnimatablesThe Animatable abstract class maps adouble to a value of a particular type.Animatable classes are stateless and immutable.<topic_end><topic_start>TweensThe Tween<T> abstract class maps a doublevalue nominally in the range 0.0-1.0 to a typed value(for example, a Color, or another double).It is an Animatable.It has a notion of an output type (T),a begin value and an end value of that type,and a way to interpolate (lerp) between the beginand end values for a given input value (the double nominally inthe range 0.0-1.0).Tween classes are stateless and immutable.<topic_end><topic_start>Composing animatablesPassing an Animatable<double> (the parent) to an Animatable’schain() method creates a new Animatable subclass that applies theparent’s mapping then the child’s mapping.<topic_end><topic_start>CurvesThe Curve abstract class maps doublesnominally in the range 0.0-1.0 to doublesnominally in the range 0.0-1.0.Curve classes are stateless and immutable.<topic_end><topic_start>AnimationsThe Animation abstract class provides avalue of a given type, a concept of animationdirection and animation status, and a listener interface toregister callbacks that get invoked when the value or status change.Some subclasses of Animation have values that never change(kAlwaysCompleteAnimation, kAlwaysDismissedAnimation,AlwaysStoppedAnimation); registering callbacks onthese has no effect as the callbacks are never called.The Animation<double> variant is special because it can be used torepresent a double nominally in the range 0.0-1.0, which is the inputexpected by Curve and Tween classes, as well as some furthersubclasses of Animation.Some Animation subclasses are stateless,merely forwarding listeners to their parents.Some are very stateful.<topic_end><topic_start>Composable animationsMost Animation subclasses take an explicit “parent”Animation<double>. They are driven by that parent.The CurvedAnimation subclass takes an Animation<double> class (theparent) and a couple of Curve classes (the forward and reversecurves) as input, and uses the value of the parent as input to thecurves to determine its output. CurvedAnimation is immutable andstateless.The ReverseAnimation subclass takes anAnimation<double> class as its parent and reversesall the values of the animation. It assumes the parentis using a value nominally in the range 0.0-1.0 and returnsa value in the range 1.0-0.0. The status and direction of the parentanimation are also reversed. ReverseAnimation is immutable andstateless.The ProxyAnimation subclass takes an Animation<double> class asits parent and merely forwards the current state of that parent.However, the parent is mutable.The TrainHoppingAnimation subclass takes two parents,and switches between them when their values cross.<topic_end><topic_start>Animation controllersThe AnimationController is a statefulAnimation<double> that uses a Ticker to give itself life.It can be started and stopped. At each tick, it takes the timeelapsed since it was started and passes it to a Simulation to obtaina value. That is then the value it reports. If the Simulationreports that at that time it has ended, then the controller stopsitself.The animation controller can be given a lower and upper bound toanimate between, and a duration.In the simple case (using forward() or reverse()), the animation controller simply does a linearinterpolation from the lower bound to the upper bound (or vice versa,for the reverse direction) over the given duration.When using repeat(), the animation controller uses a linearinterpolation between the given bounds over the given duration, butdoes not stop.When using animateTo(), the animation controller does a linearinterpolation over the given duration from the current value to thegiven target. If no duration is given to the method, the defaultduration of the controller and the range described by the controller’slower bound and upper bound is used to determine the velocity of theanimation.When using fling(), a Force is used to create a specificsimulation which is then used to drive the controller.When using animateWith(), the given simulation is used to drive thecontroller.These methods all return the future that the Ticker provides andwhich will resolve when the controller next stops or changessimulation.<topic_end><topic_start>Attaching animatables to animationsPassing an Animation<double> (the new parent) to an Animatable’sanimate() method creates a new Animation subclass that acts likethe Animatable but is driven from the given parent.<topic_end><topic_start>AccessibilityEnsuring apps are accessible to a broad range of users is an essentialpart of building a high-quality app. Applications that are poorlydesigned create barriers to people of all ages. The UN Convention onthe Rights of Persons with Disabilities states the moral and legalimperative to ensure universal access to information systems; countriesaround the world enforce accessibility as a requirement; and companiesrecognize the business advantages of maximizing access to their services.We strongly encourage you to include an accessibility checklistas a key criteria before shipping your app. Flutter is committed tosupporting developers in making their apps more accessible, and includesfirst-class framework support for accessibility in addition to thatprovided by the underlying operating system, including:Details of these features are discussed below.<topic_end><topic_start>Inspecting accessibility supportIn addition to testing for these specific topics,we recommend using automated accessibility scanners:<topic_end><topic_start>Large fontsBoth Android and iOS contain system settings to configure the desired fontsizes used by apps. Flutter text widgets respect this OS setting whendetermining font sizes.Font sizes are calculated automatically by Flutter based on the OS setting.However, as a developer you should make sure your layout has enough room torender all its contents when the font sizes are increased.For example, you can test all parts of your app on a small-screendevice configured to use the largest font setting.<topic_end><topic_start>ExampleThe following two screenshots show the standard Flutter apptemplate rendered with the default iOS font setting,and with the largest font setting selected in iOS accessibility settings.<topic_end><topic_start>Screen readersFor mobile, screen readers (TalkBack, VoiceOver)enable visually impaired users to get spoken feedback aboutthe contents of the screen and interact with the UI by usinggestures on mobile and keyboard shortcuts on desktop.Turn on VoiceOver or TalkBack on your mobile device andnavigate around your app.To turn on the screen reader on your device, complete the following steps:To learn how to find and customize Android’saccessibility features, view the following video.To learn how to find and customize iOSaccessibility features, view the following video.For web, the following screen readers are currently supported:Mobile browsers:Desktop browsers:Screen readers users on web must toggle the“Enable accessibility” button to build the semantics tree.Users can skip this step if you programmatically auto-enableaccessibility for your app using this API:Windows comes with a screen reader called Narratorbut some developers recommend using the more popularNVDA screen reader. To learn about using NVDA to testWindows apps, check outScreen Readers 101 For Front-End Developers (Windows).On a Mac, you can use the desktop version of VoiceOver,which is included in macOS.On Linux, a popular screen reader is called Orca.It comes pre-installed with some distributionsand is available on package repositories such as apt.To learn about using Orca, check outGetting started with Orca screen reader on Gnome desktop.Check out the following video demo to see Victor Tsaran,using VoiceOver with the now-archived Flutter Gallery web app.Flutter’s standard widgets generate an accessibility tree automatically.However, if your app needs something different,it can be customized using the Semantics widget.When there is text in your app that should be voicedwith a specific voice, inform the screen readerwhich voice to use by calling TextSpan.locale.Note that MaterialApp.locale and Localizations.overridedon’t affect which voice the screen reader uses.Usually, the screen reader uses the system voiceexcept where you explicitly set it with TextSpan.locale.<topic_end><topic_start>Sufficient contrastSufficient color contrast makes text and images easier to read.Along with benefitting users with various visual impairments,sufficient color contrast helps all users when viewing an interfaceon devices in extreme lighting conditions,such as when exposed to direct sunlight or on a display with lowbrightness.The W3C recommends:<topic_end><topic_start>Building with accessibility in mindEnsuring your app can be used by everyone means building accessibilityinto it from the start. For some apps, that’s easier said than done.In the video below, two of our engineers take a mobile app from a direaccessibility state to one that takes advantage of Flutter’s built-inwidgets to offer a dramatically more accessible experience.<topic_end><topic_start>Testing accessibility on mobileTest your app using Flutter’s Accessibility Guideline API.This API checks if your app’s UI meets Flutter’s accessibility recommendations.These cover recommendations for text contrast, target size, and target labels.The following example shows how to use the Guideline API on Name Generator.You created this app as part of theWrite your first Flutter app codelab.Each button on the app’s main screen serves as a tappable targetwith text represented in 18 point.<code_start>final SemanticsHandle handle = tester.ensureSemantics();await tester.pumpWidget(MyApp());// Checks that tappable nodes have a minimum size of 48 by 48 pixels// for Android.await expectLater(tester, meetsGuideline(androidTapTargetGuideline));// Checks that tappable nodes have a minimum size of 44 by 44 pixels// for iOS.await expectLater(tester, meetsGuideline(iOSTapTargetGuideline));// Checks that touch targets with a tap or long press action are labeled.await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));// Checks whether semantic nodes meet the minimum text contrast levels.// The recommended text contrast is 3:1 for larger text// (18 point and above regular).await expectLater(tester, meetsGuideline(textContrastGuideline));handle.dispose();<code_end>You can add Guideline API testsin test/widget_test.dart of your app directory, or as a separate testfile (such as test/a11y_test.dart in the case of the Name Generator).<topic_end><topic_start>Testing accessibility on webYou can debug accessibility by visualizing the semantic nodes created for your web appusing the following command line flag in profile and release modes:With the flag activated, the semantic nodes appear on top of the widgets;you can verify that the semantic elements are placed where they should be.If the semantic nodes are incorrectly placed, please file a bug report.<topic_end><topic_start>Accessibility release checklistHere is a non-exhaustive list of things to consider as you prepare yourapp for release.<topic_end><topic_start>Learn moreTo learn more about Flutter and accessibility, check outthe following articles written by community members:<topic_end><topic_start>Internationalizing Flutter apps<topic_end><topic_start>What you'll learnIf your app might be deployed to users who speak anotherlanguage then you’ll need to internationalize it.That means you need to write the app in a way that makesit possible to localize values like text and layoutsfor each language or locale that the app supports.Flutter provides widgets and classes that help withinternationalization and the Flutter librariesthemselves are internationalized.This page covers concepts and workflows necessary tolocalize a Flutter application using theMaterialApp and CupertinoApp classes,as most apps are written that way.However, applications written using the lower levelWidgetsApp class can also be internationalizedusing the same classes and logic.<topic_end><topic_start>Introduction to localizations in FlutterThis section provides a tutorial on how to create andinternationalize a new Flutter application,along with any additional setupthat a target platform might require.You can find the source code for this example ingen_l10n_example.<topic_end><topic_start>Setting up an internation­alized app: the Flutter_localizations packageBy default, Flutter only provides US English localizations.To add support for other languages,an application must specify additionalMaterialApp (or CupertinoApp) properties,and include a package called flutter_localizations.As of December 2023, this package supports 115 languagesand language variants.To begin, start by creating a new Flutter applicationin a directory of your choice with the flutter create command.To use flutter_localizations,add the package as a dependency to your pubspec.yaml file, as well as the intl package:This creates a pubspec.yml file with the following entries:<code_start>dependencies:  flutter:    sdk: flutter  flutter_localizations:    sdk: flutter  intl: any<code_end>Then import the flutter_localizations library and specifylocalizationsDelegates and supportedLocales foryour MaterialApp or CupertinoApp:<code_start>import 'package:flutter_localizations/flutter_localizations.dart';<code_end><code_start>return const MaterialApp(  title: 'Localizations Sample App',  localizationsDelegates: [    GlobalMaterialLocalizations.delegate,    GlobalWidgetsLocalizations.delegate,    GlobalCupertinoLocalizations.delegate,  ],  supportedLocales: [    Locale('en'), // English    Locale('es'), // Spanish  ],  home: MyHomePage(),);<code_end>After introducing the flutter_localizations packageand adding the previous code,the Material and Cupertinopackages should now be correctly localized inone of the 115 supported locales.Widgets should be adapted to the localized messages,along with correct left-to-right or right-to-left layout.Try switching the target platform’s locale toSpanish (es) and the messages should be localized.Apps based on WidgetsApp are similar except that theGlobalMaterialLocalizations.delegate isn’t needed.The full Locale.fromSubtags constructor is preferredas it supports scriptCode, though the Locale defaultconstructor is still fully valid.The elements of the localizationsDelegates list arefactories that produce collections of localized values.GlobalMaterialLocalizations.delegate provides localizedstrings and other values for the Material Componentslibrary. GlobalWidgetsLocalizations.delegatedefines the default text direction,either left-to-right or right-to-left, for the widgets library.More information about these app properties, the types theydepend on, and how internationalized Flutter apps are typicallystructured, is covered in this page.<topic_end><topic_start>Overriding the localeLocalizations.override is a factory constructorfor the Localizations widget that allows for(the typically rare) situation where a section of your applicationneeds to be localized to a different locale than the localeconfigured for your device.To observe this behavior, add a call to Localizations.overrideand a simple CalendarDatePicker:<code_start>Widget build(BuildContext context) {  return Scaffold(    appBar: AppBar(      title: Text(widget.title),    ),    body: Center(      child: Column(        mainAxisAlignment: MainAxisAlignment.center,        children: <Widget>[          // Add the following code          Localizations.override(            context: context,            locale: const Locale('es'),            // Using a Builder to get the correct BuildContext.            // Alternatively, you can create a new widget and Localizations.override            // will pass the updated BuildContext to the new widget.            child: Builder(              builder: (context) {                // A toy example for an internationalized Material widget.                return CalendarDatePicker(                  initialDate: DateTime.now(),                  firstDate: DateTime(1900),                  lastDate: DateTime(2100),                  onDateChanged: (value) {},                );              },            ),          ),        ],      ),    ),  );}<code_end>Hot reload the app and the CalendarDatePickerwidget should re-render in Spanish.<topic_end><topic_start>Adding your own localized messagesAfter adding the flutter_localizations package,you can configure localization.To add localized text to your application,complete the following instructions:Add the intl package as a dependency, pullingin the version pinned by flutter_localizations:Open the pubspec.yaml file and enable the generate flag. This flag is found in the flutter section in the pubspec file.<code_start># The following section is specific to Flutter.flutter:  generate: true # Add this line<code_end>Add a new yaml file to the root directory of the Flutter project.Name this file l10n.yaml and include following content:<code_start>arb-dir: lib/l10ntemplate-arb-file: app_en.arboutput-localization-file: app_localizations.dart<code_end>This file configures the localization tool.In this example, you’ve done the following:In ${FLUTTER_PROJECT}/lib/l10n,add the app_en.arb template file. For example:<code_start>{  "helloWorld": "Hello World!",  "@helloWorld": {    "description": "The conventional newborn programmer greeting"  }}<code_end>Add another bundle file called app_es.arb in the same directory.In this file, add the Spanish translation of the same message.<code_start>{    "helloWorld": "¡Hola Mundo!"}<code_end>Now, run flutter pub get or flutter run and codegen takes place automatically.You should find generated files in${FLUTTER_PROJECT}/.dart_tool/flutter_gen/gen_l10n.Alternatively, you can also run flutter gen-l10n togenerate the same files without running the app.Add the import statement on app_localizations.dart andAppLocalizations.delegatein your call to the constructor for MaterialApp:<code_start>import 'package:flutter_gen/gen_l10n/app_localizations.dart';<code_end><code_start>return const MaterialApp(  title: 'Localizations Sample App',  localizationsDelegates: [    AppLocalizations.delegate, // Add this line    GlobalMaterialLocalizations.delegate,    GlobalWidgetsLocalizations.delegate,    GlobalCupertinoLocalizations.delegate,  ],  supportedLocales: [    Locale('en'), // English    Locale('es'), // Spanish  ],  home: MyHomePage(),);<code_end>The AppLocalizations class also provides auto-generatedlocalizationsDelegates and supportedLocales lists.You can use these instead of providing them manually.<code_start>const MaterialApp(  title: 'Localizations Sample App',  localizationsDelegates: AppLocalizations.localizationsDelegates,  supportedLocales: AppLocalizations.supportedLocales,);<code_end>Once the Material app has started,you can use AppLocalizations anywhere in your app:<code_start>appBar: AppBar(  // The [AppBar] title text should update its message  // according to the system locale of the target platform.  // Switching between English and Spanish locales should  // cause this text to update.  title: Text(AppLocalizations.of(context)!.helloWorld),),<code_end>info Note  The Material app has to actually be started to initialize  AppLocalizations. If the app hasn’t yet started,  AppLocalizations.of(context)!.helloWorld causes a  null exception.This code generates a Text widget that displays “Hello World!”   if the target device’s locale is set to English,   and “¡Hola Mundo!” if the target device’s locale is set   to Spanish. In the arb files,   the key of each entry is used as the method name of the getter,   while the value of that entry contains the localized message.The gen_l10n_example uses this tool.To localize your device app description,pass the localized string toMaterialApp.onGenerateTitle:<code_start>return MaterialApp(  onGenerateTitle: (context) => DemoLocalizations.of(context).title,<code_end><topic_end><topic_start>Placeholders, plurals, and selectslightbulb Tip  When using VS Code, add the arb-editor extension.  This extension adds syntax highlighting, snippets,   diagnostics, and quick fixes to help edit .arb template files.You can also include application values in a message withspecial syntax that uses a placeholder to generate a methodinstead of a getter.A placeholder, which must be a valid Dart identifier name,becomes a positional parameter in the generated method in theAppLocalizations code. Define a placeholder name by wrappingit in curly braces as follows:Define each placeholder in the placeholders objectin the app’s .arb file. For example,to define a hello message with a userName parameter,add the following to lib/l10n/app_en.arb:<code_start>"hello": "Hello {userName}","@hello": {  "description": "A message with a single parameter",  "placeholders": {    "userName": {      "type": "String",      "example": "Bob"    }  }}<code_end>This code snippet adds a hello method call tothe AppLocalizations.of(context) object,and the method accepts a parameter of type String;the hello method returns a string.Regenerate the AppLocalizations file.Replace the code passed into Builder with the following:<code_start>// Examples of internationalized strings.return Column(  children: <Widget>[    // Returns 'Hello John'    Text(AppLocalizations.of(context)!.hello('John')),  ],);<code_end>You can also use numerical placeholders to specify multiple values.Different languages have different ways to pluralize words.The syntax also supports specifying how a word should be pluralized.A pluralized message must include a num parameter indicatinghow to pluralize the word in different situations.English, for example, pluralizes “person” to “people”,but that doesn’t go far enough.The message0 plural might be “no people” or “zero people”.The messageFew plural might be“several people”, “some people”, or “a few people”. The messageMany plural mightbe  “most people” or “many people”, or “a crowd”. Only the more general messageOther field is required.The following example shows what options are available:The previous expression is replaced by the message variation(message0, message1, …) corresponding to the valueof the countPlaceholder.Only the messageOther field is required.The following example defines a message that pluralizesthe word, “wombat”:<code_start>"nWombats": "{count, plural, =0{no wombats} =1{1 wombat} other{{count} wombats}}","@nWombats": {  "description": "A plural message",  "placeholders": {    "count": {      "type": "num",      "format": "compact"    }  }}<code_end>Use a plural method by passing in the count parameter:<code_start>// Examples of internationalized strings.return Column(  children: <Widget>[    ...    // Returns 'no wombats'    Text(AppLocalizations.of(context)!.nWombats(0)),    // Returns '1 wombat'    Text(AppLocalizations.of(context)!.nWombats(1)),    // Returns '5 wombats'    Text(AppLocalizations.of(context)!.nWombats(5)),  ],);<code_end>Similar to plurals,you can also choose a value based on a String placeholder.This is most often used to support gendered languages.The syntax is as follows:The next example defines a message thatselects a pronoun based on gender:<code_start>"pronoun": "{gender, select, male{he} female{she} other{they}}","@pronoun": {  "description": "A gendered message",  "placeholders": {    "gender": {      "type": "String"    }  }}<code_end>Use this feature bypassing the gender string as a parameter:<code_start>// Examples of internationalized strings.return Column(  children: <Widget>[    ...    // Returns 'he'    Text(AppLocalizations.of(context)!.pronoun('male')),    // Returns 'she'    Text(AppLocalizations.of(context)!.pronoun('female')),    // Returns 'they'    Text(AppLocalizations.of(context)!.pronoun('other')),  ],);<code_end>Keep in mind that when using select statements,comparison between the parameter and the actualvalue is case-sensitive.That is, AppLocalizations.of(context)!.pronoun("Male")defaults to the “other” case, and returns “they”.<topic_end><topic_start>Escaping syntaxSometimes, you have to use tokens,such as { and }, as normal characters.To ignore such tokens from being parsed,enable the use-escaping flag by adding thefollowing to l10n.yaml:The parser ignores any string of characterswrapped with a pair of single quotes.To use a normal single quote character,use a pair of consecutive single quotes.For example, the follow text is convertedto a Dart String:The resulting string is as follows:<topic_end><topic_start>Messages with numbers and currenciesNumbers, including those that represent currency values,are displayed very differently in different locales. The localizations generation tool influtter_localizations uses theNumberFormatclass in the intl package to formatnumbers based on the locale and the desired format.The int, double, and number types can use any of thefollowing NumberFormat constructors:The starred NumberFormat constructors in the tableoffer optional, named parameters.Those parameters can be specified as the valueof the placeholder’s optionalParameters object.For example, to specify the optional decimalDigitsparameter for compactCurrency,make the following changes to the lib/l10n/app_en.arg file:<code_start>"numberOfDataPoints": "Number of data points: {value}","@numberOfDataPoints": {  "description": "A message with a formatted int parameter",  "placeholders": {    "value": {      "type": "int",      "format": "compactCurrency",      "optionalParameters": {        "decimalDigits": 2      }    }  }}<code_end><topic_end><topic_start>Messages with datesDates strings are formatted in many different waysdepending both the locale and the app’s needs.Placeholder values with type DateTime are formatted withDateFormat in the intl package.There are 41 format variations,identified by the names of their DateFormat factory constructors.In the following example, the DateTime valuethat appears in the helloWorldOn message isformatted with DateFormat.yMd:In an app where the locale is US English,the following expression would produce  “7/9/1959”.In a Russian locale, it would produce “9.07.1959”.<topic_end><topic_start>Localizing for iOS: Updating the iOS app bundleTypically, iOS applications define key application metadata,including supported locales, in an Info.plist filethat is built into the application bundle.To configure the locales supported by your app,use the following instructions:Open your project’s ios/Runner.xcworkspace Xcode file.In the Project Navigator, open the Info.plist fileunder the Runner project’s Runner folder.Select the Information Property List item.Then select Add Item from the Editor menu,and select Localizations from the pop-up menu.Select and expand the newly-created Localizations item.For each locale your application supports,add a new item and select the locale you wish to addfrom the pop-up menu in the Value field.This list should be consistent with the languages listedin the supportedLocales parameter.Once all supported locales have been added, save the file.<topic_end><topic_start>Advanced topics for further customizationThis section covers additional ways to customize alocalized Flutter application.<topic_end><topic_start>Advanced locale definitionSome languages with multiple variants require more than just alanguage code to properly differentiate.For example, fully differentiating all variants ofChinese requires specifying the language code, script code,and country code. This is due to the existenceof simplified and traditional script, as well as regionaldifferences in the way characters are written within the same script type.In order to fully express every variant of Chinese for thecountry codes CN, TW, and HK, the list of supportedlocales should include:<code_start>supportedLocales: [  Locale.fromSubtags(languageCode: 'zh'), // generic Chinese 'zh'  Locale.fromSubtags(      languageCode: 'zh',      scriptCode: 'Hans'), // generic simplified Chinese 'zh_Hans'  Locale.fromSubtags(      languageCode: 'zh',      scriptCode: 'Hant'), // generic traditional Chinese 'zh_Hant'  Locale.fromSubtags(      languageCode: 'zh',      scriptCode: 'Hans',      countryCode: 'CN'), // 'zh_Hans_CN'  Locale.fromSubtags(      languageCode: 'zh',      scriptCode: 'Hant',      countryCode: 'TW'), // 'zh_Hant_TW'  Locale.fromSubtags(      languageCode: 'zh',      scriptCode: 'Hant',      countryCode: 'HK'), // 'zh_Hant_HK'],<code_end>This explicit full definition ensures that your app candistinguish between and provide the fully nuanced localizedcontent to all combinations of these country codes.If a user’s preferred locale isn’t specified,Flutter selects the closest match,which likely contains differences to what the user expects.Flutter only resolves to locales defined in supportedLocalesand provides scriptCode-differentiated localizedcontent for commonly used languages.See Localizations for information on how the supportedlocales and the preferred locales are resolved.Although Chinese is a primary example,other languages like French (fr_FR, fr_CA)should also be fully differentiated for more nuanced localization.<topic_end><topic_start>Tracking the locale: The Locale class and the Localizations widgetThe Locale class identifies the user’s language.Mobile devices support setting the locale for all applications,usually using a system settings menu.Internationalized apps respond by displaying values that arelocale-specific. For example, if the user switches the device’s localefrom English to French, then a Text widget that originallydisplayed “Hello World” would be rebuilt with “Bonjour le monde”.The Localizations widget defines the localefor its child and the localized resources that the child depends on.The WidgetsApp widget creates a Localizations widgetand rebuilds it if the system’s locale changes.You can always look up an app’s current locale withLocalizations.localeOf():<code_start>Locale myLocale = Localizations.localeOf(context);<code_end><topic_end><topic_start>Specifying the app’s supported­Locales parameterAlthough the flutter_localizations library currently supports115 languages and language variants, only English language translationsare available by default. It’s up to the developer to decide exactlywhich languages to support.The MaterialApp supportedLocalesparameter limits locale changes. When the user changes the localesetting on their device, the app’s Localizations widget onlyfollows suit if the new locale is a member of this list.If an exact match for the device locale isn’t found,then the first supported locale with a matching languageCodeis used. If that fails, then the first element of thesupportedLocales list is used.An app that wants to use a different “locale resolution”method can provide a localeResolutionCallback.For example, to have your app unconditionally acceptwhatever locale the user selects:<code_start>MaterialApp(  localeResolutionCallback: (    locale,    supportedLocales,  ) {    return locale;  },);<code_end><topic_end><topic_start>Configuring the l10n.yaml fileThe l10n.yaml file allows you to configure the gen-l10n toolto specify the following:For a full list of options, either run flutter gen-l10n --helpat the command line or refer to the following table:<topic_end><topic_start>How internationalization in Flutter worksThis section covers the technical details of how localizations workin Flutter. If you’re planning on supporting your own set of localizedmessages, the following content would be helpful.Otherwise, you can skip this section.<topic_end><topic_start>Loading and retrieving localized valuesThe Localizations widget is used to load andlook up objects that contain collections of localized values.Apps refer to these objects with Localizations.of(context,type).If the device’s locale changes,the Localizations widget automatically loads values forthe new locale and then rebuilds widgets that used it.This happens because Localizations works like anInheritedWidget.When a build function refers to an inherited widget,an implicit dependency on the inherited widget is created.When an inherited widget changes(when the Localizations widget’s locale changes),its dependent contexts are rebuilt.Localized values are loaded by the Localizations widget’slist of LocalizationsDelegates.Each delegate must define an asynchronous load()method that produces an object that encapsulates acollection of localized values.Typically these objects define one method per localized value.In a large app, different modules or packages might be bundled withtheir own localizations. That’s why the Localizations widgetmanages a table of objects, one per LocalizationsDelegate.To retrieve the object produced by one of the LocalizationsDelegate’sload methods, specify a BuildContext and the object’s type.For example,the localized strings for the Material Components widgetsare defined by the MaterialLocalizations class.Instances of this class are created by a LocalizationDelegateprovided by the MaterialApp class.They can be retrieved with Localizations.of():This particular Localizations.of() expression is used frequently,so the MaterialLocalizations class provides a convenient shorthand:<topic_end><topic_start>Defining a class for the app’s localized resourcesPutting together an internationalized Flutter app usuallystarts with the class that encapsulates the app’s localized values.The example that follows is typical of such classes.Complete source code for the intl_example for this app.This example is based on the APIs and tools provided by theintl package. The An alternative class for the app’slocalized resources sectiondescribes an example that doesn’t depend on the intl package.The DemoLocalizations class(defined in the following code snippet)contains the app’s strings (just one for the example)translated into the locales that the app supports.It uses the initializeMessages() functiongenerated by Dart’s intl package,Intl.message(), to look them up.<code_start>class DemoLocalizations {  DemoLocalizations(this.localeName);  static Future<DemoLocalizations> load(Locale locale) {    final String name =        locale.countryCode == null || locale.countryCode!.isEmpty            ? locale.languageCode            : locale.toString();    final String localeName = Intl.canonicalizedLocale(name);    return initializeMessages(localeName).then((_) {      return DemoLocalizations(localeName);    });  }  static DemoLocalizations of(BuildContext context) {    return Localizations.of<DemoLocalizations>(context, DemoLocalizations)!;  }  final String localeName;  String get title {    return Intl.message(      'Hello World',      name: 'title',      desc: 'Title for the Demo application',      locale: localeName,    );  }}<code_end>A class based on the intl package imports a generatedmessage catalog that provides the initializeMessages()function and the per-locale backing store for Intl.message().The message catalog is produced by an intl toolthat analyzes the source code for classes that containIntl.message() calls.In this case that would just be the DemoLocalizations class.<topic_end><topic_start>Adding support for a new languageAn app that needs to support a language that’s not included inGlobalMaterialLocalizations has to do some extra work:it must provide about 70 translations (“localizations”)for words or phrases and the date patterns and symbols for thelocale.See the following for an example of how to addsupport for the Norwegian Nynorsk language.A new GlobalMaterialLocalizations subclass defines thelocalizations that the Material library depends on.A new LocalizationsDelegate subclass, which servesas factory for the GlobalMaterialLocalizations subclass,must also be defined.Here’s the source code for the complete add_language example,minus the actual Nynorsk translations.The locale-specific GlobalMaterialLocalizations subclassis called NnMaterialLocalizations,and the LocalizationsDelegate subclass is_NnMaterialLocalizationsDelegate.The value of NnMaterialLocalizations.delegateis an instance of the delegate, and is allthat’s needed by an app that uses these localizations.The delegate class includes basic date and number formatlocalizations. All of the other localizations are defined by Stringvalued property getters in NnMaterialLocalizations, like this:<code_start>@overrideString get moreButtonTooltip => r'More';@overrideString get aboutListTileTitleRaw => r'About $applicationName';@overrideString get alertDialogLabel => r'Alert';<code_end>These are the English translations, of course.To complete the job you need to change the returnvalue of each getter to an appropriate Nynorsk string.The getters return “raw” Dart strings that have an r prefix,such as r'About $applicationName',because sometimes the strings contain variables with a $ prefix.The variables are expanded by parameterized localization methods:<code_start>@overrideString get pageRowsInfoTitleRaw => r'$firstRow–$lastRow of $rowCount';@overrideString get pageRowsInfoTitleApproximateRaw =>    r'$firstRow–$lastRow of about $rowCount';<code_end>The date patterns and symbols of the locale also need tobe specified, which are defined in the source code as follows:<code_start>const nnLocaleDatePatterns = {  'd': 'd.',  'E': 'ccc',  'EEEE': 'cccc',  'LLL': 'LLL',  // ...}<code_end><code_start>const nnDateSymbols = {  'NAME': 'nn',  'ERAS': <dynamic>[    'f.Kr.',    'e.Kr.',  ],  // ...}<code_end>These values need to be modified for the locale to use the correctdate formatting. Unfortunately, since the intl library doesn’tshare the same flexibility for number formatting,the formatting for an existing locale must be usedas a substitute in _NnMaterialLocalizationsDelegate:<code_start>class _NnMaterialLocalizationsDelegate    extends LocalizationsDelegate<MaterialLocalizations> {  const _NnMaterialLocalizationsDelegate();  @override  bool isSupported(Locale locale) => locale.languageCode == 'nn';  @override  Future<MaterialLocalizations> load(Locale locale) async {    final String localeName = intl.Intl.canonicalizedLocale(locale.toString());    // The locale (in this case `nn`) needs to be initialized into the custom    // date symbols and patterns setup that Flutter uses.    date_symbol_data_custom.initializeDateFormattingCustom(      locale: localeName,      patterns: nnLocaleDatePatterns,      symbols: intl.DateSymbols.deserializeFromMap(nnDateSymbols),    );    return SynchronousFuture<MaterialLocalizations>(      NnMaterialLocalizations(        localeName: localeName,        // The `intl` library's NumberFormat class is generated from CLDR data        // (see https://github.com/dart-lang/i18n/blob/main/pkgs/intl/lib/number_symbols_data.dart).        // Unfortunately, there is no way to use a locale that isn't defined in        // this map and the only way to work around this is to use a listed        // locale's NumberFormat symbols. So, here we use the number formats        // for 'en_US' instead.        decimalFormat: intl.NumberFormat('#,##0.###', 'en_US'),        twoDigitZeroPaddedFormat: intl.NumberFormat('00', 'en_US'),        // DateFormat here will use the symbols and patterns provided in the        // `date_symbol_data_custom.initializeDateFormattingCustom` call above.        // However, an alternative is to simply use a supported locale's        // DateFormat symbols, similar to NumberFormat above.        fullYearFormat: intl.DateFormat('y', localeName),        compactDateFormat: intl.DateFormat('yMd', localeName),        shortDateFormat: intl.DateFormat('yMMMd', localeName),        mediumDateFormat: intl.DateFormat('EEE, MMM d', localeName),        longDateFormat: intl.DateFormat('EEEE, MMMM d, y', localeName),        yearMonthFormat: intl.DateFormat('MMMM y', localeName),        shortMonthDayFormat: intl.DateFormat('MMM d'),      ),    );  }  @override  bool shouldReload(_NnMaterialLocalizationsDelegate old) => false;}<code_end>For more information about localization strings,check out the flutter_localizations README.Once you’ve implemented your language-specific subclasses ofGlobalMaterialLocalizations and LocalizationsDelegate,you  need to add the language and a delegate instance to your app.The following code sets the app’s language to Nynorsk andadds the NnMaterialLocalizations delegate instance to the app’slocalizationsDelegates list:<code_start>const MaterialApp(  localizationsDelegates: [    GlobalWidgetsLocalizations.delegate,    GlobalMaterialLocalizations.delegate,    NnMaterialLocalizations.delegate, // Add the newly created delegate  ],  supportedLocales: [    Locale('en', 'US'),    Locale('nn'),  ],  home: Home(),),<code_end><topic_end><topic_start>Alternative internationalization workflowsThis section describes different approaches to internationalizeyour Flutter application.<topic_end><topic_start>An alternative class for the app’s localized resourcesThe previous example was defined in terms of the Dart intlpackage. You can choose your own approach for managinglocalized values for the sake of simplicity or perhaps to integratewith a different i18n framework.Complete source code for the minimal app.In the following example, the DemoLocalizations class includes all of its translations directly in per language Maps:<code_start>class DemoLocalizations {  DemoLocalizations(this.locale);  final Locale locale;  static DemoLocalizations of(BuildContext context) {    return Localizations.of<DemoLocalizations>(context, DemoLocalizations)!;  }  static const _localizedValues = <String, Map<String, String>>{    'en': {      'title': 'Hello World',    },    'es': {      'title': 'Hola Mundo',    },  };  static List<String> languages() => _localizedValues.keys.toList();  String get title {    return _localizedValues[locale.languageCode]!['title']!;  }}<code_end>In the minimal app the DemoLocalizationsDelegate is slightlydifferent. Its load method returns a SynchronousFuturebecause no asynchronous loading needs to take place.<code_start>class DemoLocalizationsDelegate    extends LocalizationsDelegate<DemoLocalizations> {  const DemoLocalizationsDelegate();  @override  bool isSupported(Locale locale) =>      DemoLocalizations.languages().contains(locale.languageCode);  @override  Future<DemoLocalizations> load(Locale locale) {    // Returning a SynchronousFuture here because an async "load" operation    // isn't needed to produce an instance of DemoLocalizations.    return SynchronousFuture<DemoLocalizations>(DemoLocalizations(locale));  }  @override  bool shouldReload(DemoLocalizationsDelegate old) => false;}<code_end><topic_end><topic_start>Using the Dart intl toolsBefore building an API using the Dart intl package,review the intl package’s documentation.The following list summarizes the process forlocalizing an app that depends on the intl package:The demo app depends on a generated source file calledl10n/messages_all.dart, which defines all of thelocalizable strings used by the app.Rebuilding l10n/messages_all.dart requires two steps.With the app’s root directory as the current directory,generate l10n/intl_messages.arb from lib/main.dart:The intl_messages.arb file is a JSON format map with one entry foreach Intl.message() function defined in main.dart.This file serves as a template for the English and Spanish translations,intl_en.arb and intl_es.arb.These translations are created by you, the developer.With the app’s root directory as the current directory,generate intl_messages_<locale>.dart for eachintl_<locale>.arb file and intl_messages_all.dart,which imports all of the messages files:Windows doesn’t support file name wildcarding.Instead, list the .arb files that were generated by theintl_translation:extract_to_arb command.The DemoLocalizations class uses the generatedinitializeMessages() function(defined in intl_messages_all.dart)to load the localized messages and Intl.message()to look them up.<topic_end><topic_start>More informationIf you learn best by reading code,check out the following examples.If Dart’s intl package is new to you,check out Using the Dart intl tools.<topic_end><topic_start>State management<topic_end><topic_start>Topics<topic_end><topic_start>State managementinfo Note  If you have written a mobile app using Flutter  and wonder why your app’s state is lost  on a restart, check out Restore state on Android  or Restore state on iOS.If you are already familiar with state management in reactive apps,you can skip this section, though you might want to review thelist of different approaches.As you explore Flutter,there comes a time when you need to share applicationstate between screens, across your app.There are many approaches you can take,and many questions to think about.In the following pages,you will learn the basics of dealing with state in Flutter apps.<topic_end><topic_start>Start thinking declarativelyIf you’re coming to Flutter from an imperative framework(such as Android SDK or iOS UIKit), you need to startthinking about app development from a new perspective.Many assumptions that you might have don’t apply to Flutter. For example, inFlutter it’s okay to rebuild parts of your UI from scratch instead of modifyingit. Flutter is fast enough to do that, even on every frame if needed.Flutter is declarative. This means that Flutter builds its user interface toreflect the current state of your app:When the state of your app changes(for example, the user flips a switch in the settings screen),you change the state, and that triggers a redraw of the user interface.There is no imperative changing of the UI itself(like widget.setText)—you change the state,and the UI rebuilds from scratch.Read more about the declarative approach to UI programmingin the get started guide.The declarative style of UI programming has many benefits.Remarkably, there is only one code path for any state of the UI.You describe what the UI should looklike for any given state, once—and that is it.At first,this style of programming might not seem as intuitive as theimperative style. This is why this section is here. Read on.<topic_end><topic_start>Differentiate between ephemeral state and app stateThis doc introduces app state, ephemeral state,and how you might manage each in a Flutter app.In the broadest possible sense, the state of an app is everything thatexists in memory when the app is running. This includes the app’s assets,all the variables that the Flutter framework keeps about the UI,animation state, textures, fonts, and so on. While this broadestpossible definition of state is valid, it’s not very useful forarchitecting an app.First, you don’t even manage some state (like textures).The framework handles those for you. So a more useful definition ofstate is “whatever data you need in order to rebuild your UI at anymoment in time”. Second, the state that you do manage yourself canbe separated into two conceptual types: ephemeral state and app state.<topic_end><topic_start>Ephemeral stateEphemeral state (sometimes called UI state or local state)is the state you can neatly contain in a single widget.This is, intentionally, a vague definition, so here are a few examples.Other parts of the widget tree seldom need to access this kind of state.There is no need to serialize it, and it doesn’t change in complex ways.In other words, there is no need to use state management techniques(ScopedModel, Redux, etc.) on this kind of state.All you need is a StatefulWidget.Below, you see how the currently selected item in a bottom navigation bar isheld in the _index field of the _MyHomepageState class.In this example, _index is ephemeral state.<code_start>class MyHomepage extends StatefulWidget {  const MyHomepage({super.key});  @override  State<MyHomepage> createState() => _MyHomepageState();}class _MyHomepageState extends State<MyHomepage> {  int _index = 0;  @override  Widget build(BuildContext context) {    return BottomNavigationBar(      currentIndex: _index,      onTap: (newIndex) {        setState(() {          _index = newIndex;        });      },      // ... items ...    );  }}<code_end>Here, using setState() and a field inside the StatefulWidget’s Stateclass is completely natural. No other part of your app needs to access_index. The variable only changes inside the MyHomepage widget.And, if the user closes and restarts the app,you don’t mind that _index resets to zero.<topic_end><topic_start>App stateState that is not ephemeral,that you want to share across many parts of your app,and that you want to keep between user sessions,is what we call application state(sometimes also called shared state).Examples of application state:For managing app state, you’ll want to research your options.Your choice depends on the complexity and nature of your app,your team’s previous experience, and many other aspects. Read on.<topic_end><topic_start>There is no clear-cut ruleTo be clear, you can use State and setState() to manage all ofthe state in your app. In fact, the Flutter team does this in manysimple app samples (including the starter app that you get with everyflutter create).It goes the other way, too. For example, you might decide that—inthe context of your particular app—the selected tab in a bottomnavigation bar is not ephemeral state. You might need to change itfrom outside the class, keep it between sessions, and so on.In that case, the _index variable is app state.There is no clear-cut, universal rule to distinguishwhether a particular variable is ephemeral or app state.Sometimes, you’ll have to refactor one into another.For example, you’ll start with some clearly ephemeral state,but as your application grows in features,it might need to be moved to app state.For that reason, take the following diagram with a large grain of salt:When asked about React’s setState versus Redux’s store, the author of Redux,Dan Abramov, replied:“The rule of thumb is: Do whatever is less awkward.”In summary, there are two conceptual types of state in any Flutter app.Ephemeral state can be implemented using State and setState(),and is often local to a single widget. The rest is your app state.Both types have their place in any Flutter app, and the split betweenthe two depends on your own preference and the complexity of the app.<topic_end><topic_start>Simple app state managementNow that you know about declarative UI programmingand the difference between ephemeral and app state,you are ready to learn about simple app state management.On this page, we are going to be using the provider package.If you are new to Flutter and you don’t have a strong reason to chooseanother approach (Redux, Rx, hooks, etc.), this is probably the approachyou should start with. The provider package is easy to understandand it doesn’t use much code.It also uses concepts that are applicable in every other approach.That said, if you have a strong background instate management from other reactive frameworks,you can find packages and tutorials listed on the options page.<topic_end><topic_start>Our exampleFor illustration, consider the following simple app.The app has two separate screens: a catalog,and a cart (represented by the MyCatalog,and MyCart widgets, respectively). It could be a shopping app,but you can imagine the same structure in a simple social networkingapp (replace catalog for “wall” and cart for “favorites”).The catalog screen includes a custom app bar (MyAppBar)and a scrolling view of many list items (MyListItems).Here’s the app visualized as a widget tree.So we have at least 5 subclasses of Widget. Many of them needaccess to state that “belongs” elsewhere. For example, eachMyListItem needs to be able to add itself to the cart.It might also want to see whether the currently displayed itemis already in the cart.This takes us to our first question: where should we put the currentstate of the cart?<topic_end><topic_start>Lifting state upIn Flutter,it makes sense to keep the state above the widgets that use it.Why? In declarative frameworks like Flutter, if you want to change the UI,you have to rebuild it. There is no easy way to haveMyCart.updateWith(somethingNew). In other words, it’s hard toimperatively change a widget from outside, by calling a method on it.And even if you could make this work, you would be fighting theframework instead of letting it help you.Even if you get the above code to work,you would then have to dealwith the following in the MyCart widget:You would need to take into consideration the current state of the UIand apply the new data to it. It’s hard to avoid bugs this way.In Flutter, you construct a new widget every time its contents change.Instead of MyCart.updateWith(somethingNew) (a method call)you use MyCart(contents) (a constructor). Because you can onlyconstruct new widgets in the build methods of their parents,if you want to change contents, it needs to live in MyCart’sparent or above.<code_start>// GOODvoid myTapHandler(BuildContext context) {  var cartModel = somehowGetMyCartModel(context);  cartModel.add(item);}<code_end>Now MyCart has only one code path for building any version of the UI.<code_start>// GOODWidget build(BuildContext context) {  var cartModel = somehowGetMyCartModel(context);  return SomeWidget(    // Just construct the UI once, using the current state of the cart.    // ···  );}<code_end>In our example, contents needs to live in MyApp. Whenever it changes,it rebuilds MyCart from above (more on that later). Because of this,MyCart doesn’t need to worry about lifecycle—it just declareswhat to show for any given contents. When that changes, the oldMyCart widget disappears and is completely replaced by the new one.This is what we mean when we say that widgets are immutable.They don’t change—they get replaced.Now that we know where to put the state of the cart, let’s see howto access it.<topic_end><topic_start>Accessing the stateWhen a user clicks on one of the items in the catalog,it’s added to the cart. But since the cart lives above MyListItem,how do we do that?A simple option is to provide a callback that MyListItem can callwhen it is clicked. Dart’s functions are first class objects,so you can pass them around any way you want. So, insideMyCatalog you can define the following:<code_start>@overrideWidget build(BuildContext context) {  return SomeWidget(    // Construct the widget, passing it a reference to the method above.    MyListItem(myTapCallback),  );}void myTapCallback(Item item) {  print('user tapped on $item');}<code_end>This works okay, but for an app state that you need to modify frommany different places, you’d have to pass around a lot ofcallbacks—which gets old pretty quickly.Fortunately, Flutter has mechanisms for widgets to provide data andservices to their descendants (in other words, not just their children,but any widgets below them). As you would expect from Flutter,where Everything is a Widget™, these mechanisms are just specialkinds of widgets—InheritedWidget, InheritedNotifier,InheritedModel, and more. We won’t be covering those here,because they are a bit low-level for what we’re trying to do.Instead, we are going to use a package that works with the low-levelwidgets but is simple to use. It’s called provider.Before working with provider,don’t forget to add the dependency on it to your pubspec.yaml.To add the provider package as a dependency, run flutter pub add:Now you can import 'package:provider/provider.dart';and start building.With provider, you don’t need to worry about callbacks orInheritedWidgets. But you do need to understand 3 concepts:<topic_end><topic_start>ChangeNotifierChangeNotifier is a simple class included in the Flutter SDK which provideschange notification to its listeners. In other words, if something isa ChangeNotifier, you can subscribe to its changes. (It is a form ofObservable, for those familiar with the term.)In provider, ChangeNotifier is one way to encapsulate your applicationstate. For very simple apps, you get by with a single ChangeNotifier.In complex ones, you’ll have several models, and therefore severalChangeNotifiers. (You don’t need to use ChangeNotifier with providerat all, but it’s an easy class to work with.)In our shopping app example, we want to manage the state of the cart in aChangeNotifier. We create a new class that extends it, like so:<code_start>class CartModel extends ChangeNotifier {  /// Internal, private state of the cart.  final List<Item> _items = [];  /// An unmodifiable view of the items in the cart.  UnmodifiableListView<Item> get items => UnmodifiableListView(_items);  /// The current total price of all items (assuming all items cost $42).  int get totalPrice => _items.length * 42;  /// Adds [item] to cart. This and [removeAll] are the only ways to modify the  /// cart from the outside.  void add(Item item) {    _items.add(item);    // This call tells the widgets that are listening to this model to rebuild.    notifyListeners();  }  /// Removes all items from the cart.  void removeAll() {    _items.clear();    // This call tells the widgets that are listening to this model to rebuild.    notifyListeners();  }}<code_end>The only code that is specific to ChangeNotifier is the callto notifyListeners(). Call this method any time the model changes in a waythat might change your app’s UI. Everything else in CartModel is themodel itself and its business logic.ChangeNotifier is part of flutter:foundation and doesn’t depend onany higher-level classes in Flutter. It’s easily testable (you don’t even needto use widget testing for it). For example,here’s a simple unit test of CartModel:<code_start>test('adding item increases total cost', () {  final cart = CartModel();  final startingPrice = cart.totalPrice;  var i = 0;  cart.addListener(() {    expect(cart.totalPrice, greaterThan(startingPrice));    i++;  });  cart.add(Item('Dash'));  expect(i, 1);});<code_end><topic_end><topic_start>ChangeNotifierProviderChangeNotifierProvider is the widget that provides an instance ofa ChangeNotifier to its descendants. It comes from the provider package.We already know where to put ChangeNotifierProvider: above the widgets thatneed to access it. In the case of CartModel, that means somewhereabove both MyCart and MyCatalog.You don’t want to place ChangeNotifierProvider higher than necessary(because you don’t want to pollute the scope). But in our case,the only widget that is on top of both MyCart and MyCatalog is MyApp.<code_start>void main() {  runApp(    ChangeNotifierProvider(      create: (context) => CartModel(),      child: const MyApp(),    ),  );}<code_end>Note that we’re defining a builder that creates a new instanceof CartModel. ChangeNotifierProvider is smart enough not to rebuildCartModel unless absolutely necessary. It also automatically callsdispose() on CartModel when the instance is no longer needed.If you want to provide more than one class, you can use MultiProvider:<code_start>void main() {  runApp(    MultiProvider(      providers: [        ChangeNotifierProvider(create: (context) => CartModel()),        Provider(create: (context) => SomeOtherClass()),      ],      child: const MyApp(),    ),  );}<code_end><topic_end><topic_start>ConsumerNow that CartModel is provided to widgets in our app through theChangeNotifierProvider declaration at the top, we can start using it.This is done through the Consumer widget.<code_start>return Consumer<CartModel>(  builder: (context, cart, child) {    return Text('Total price: ${cart.totalPrice}');  },);<code_end>We must specify the type of the model that we want to access.In this case, we want CartModel, so we writeConsumer<CartModel>. If you don’t specify the generic (<CartModel>),the provider package won’t be able to help you. provider is based on types,and without the type, it doesn’t know what you want.The only required argument of the Consumer widgetis the builder. Builder is a function that is called whenever theChangeNotifier changes. (In other words, when you call notifyListeners()in your model, all the builder methods of all the correspondingConsumer widgets are called.)The builder is called with three arguments. The first one is context,which you also get in every build method.The second argument of the builder function is the instance ofthe ChangeNotifier. It’s what we were asking for in the first place.You can use the data in the model to define what the UI should look likeat any given point.The third argument is child, which is there for optimization.If you have a large widget subtree under your Consumerthat doesn’t change when the model changes, you can construct itonce and get it through the builder.<code_start>return Consumer<CartModel>(  builder: (context, cart, child) => Stack(    children: [      // Use SomeExpensiveWidget here, without rebuilding every time.      if (child != null) child,      Text('Total price: ${cart.totalPrice}'),    ],  ),  // Build the expensive widget here.  child: const SomeExpensiveWidget(),);<code_end>It is best practice to put your Consumer widgets as deep in the treeas possible. You don’t want to rebuild large portions of the UIjust because some detail somewhere changed.<code_start>// DON'T DO THISreturn Consumer<CartModel>(  builder: (context, cart, child) {    return HumongousWidget(      // ...      child: AnotherMonstrousWidget(        // ...        child: Text('Total price: ${cart.totalPrice}'),      ),    );  },);<code_end>Instead:<code_start>// DO THISreturn HumongousWidget(  // ...  child: AnotherMonstrousWidget(    // ...    child: Consumer<CartModel>(      builder: (context, cart, child) {        return Text('Total price: ${cart.totalPrice}');      },    ),  ),);<code_end><topic_end><topic_start>Provider.ofSometimes, you don’t really need the data in the model to change theUI but you still need to access it. For example, a ClearCartbutton wants to allow the user to remove everything from the cart.It doesn’t need to display the contents of the cart,it just needs to call the clear() method.We could use Consumer<CartModel> for this,but that would be wasteful. We’d be asking the framework torebuild a widget that doesn’t need to be rebuilt.For this use case, we can use Provider.of,with the listen parameter set to false.<code_start>Provider.of<CartModel>(context, listen: false).removeAll();<code_end>Using the above line in a build method won’t cause this widget torebuild when notifyListeners is called.<topic_end><topic_start>Putting it all togetherYou can check out the example covered in this article.If you want something simpler,see what the simple Counter app looks like whenbuilt with provider.By following along with these articles, you’ve greatly improved your ability to create state-based applications. Try building an application with provider yourself to master these skills.<topic_end><topic_start>List of state management approachesState management is a complex topic.If you feel that some of your questions haven’t been answered,or that the approach described on these pagesis not viable for your use cases, you are probably right.Learn more at the following links,many of which have been contributed by the Flutter community:<topic_end><topic_start>General overviewThings to review before selecting an approach.<topic_end><topic_start>Provider<topic_end><topic_start>RiverpodRiverpod works in a similar fashion to Provider.It offers compile safety and testing without depending on the Flutter SDK.<topic_end><topic_start>setStateThe low-level approach to use for widget-specific, ephemeral state.<topic_end><topic_start>ValueNotifier & InheritedNotifierAn approach using only Flutter provided tooling to update state and notify the UI of changes.<topic_end><topic_start>InheritedWidget & InheritedModelThe low-level approach used to communicate between ancestors and childrenin the widget tree. This is what provider and many other approachesuse under the hood.The following instructor-led video workshop covers how touse InheritedWidget:Other useful docs include:<topic_end><topic_start>JuneA lightweight and modern state management library that focuses on providinga pattern similar to Flutter’s built-in state management.<topic_end><topic_start>ReduxA state container approach familiar to many web developers.<topic_end><topic_start>Fish-ReduxFish Redux is an assembled flutter application frameworkbased on Redux state management.It is suitable for building medium and large applications.<topic_end><topic_start>BLoC / RxA family of stream/observable based patterns.<topic_end><topic_start>GetItA service locator based state management approach thatdoesn’t need a BuildContext.info Note  To learn more, watch this short Package of the Week video on the GetIt package:<topic_end><topic_start>MobXA popular library based on observables and reactions.<topic_end><topic_start>Flutter CommandsReactive state management that uses the Command Patternand is based on ValueNotifiers. Best in combination withGetIt, but can be used with Provider or otherlocators too.<topic_end><topic_start>BinderA state management package that uses InheritedWidgetat its core. Inspired in part by recoil.This package promotes the separation of concerns.<topic_end><topic_start>GetXA simplified reactive state management solution.<topic_end><topic_start>states_rebuilderAn approach that combines state management with adependency injection solution and an integrated router.For more information, see the following info:<topic_end><topic_start>Triple Pattern (Segmented State Pattern)Triple is a pattern for state management that uses Streams or ValueNotifier.This mechanism (nicknamed triple because the stream always uses threevalues: Error, Loading, and State), is based on theSegmented State pattern.For more information, refer to the following resources:<topic_end><topic_start>solidartA simple but powerful state management solution inspired by SolidJS.<topic_end><topic_start>flutter_reactive_valueThe flutter_reactive_value library might offer the least complex solution for statemanagement in Flutter. It might help Flutter newcomers add reactivity to their UI,without the complexity of the mechanisms described before.The flutter_reactive_value library defines the reactiveValue(BuildContext)extension method on ValueNotifier. This extension allows a Widget tofetch the current value of the ValueNotifier andsubscribe the Widget to changes in the value of the ValueNotifier.If the value of the ValueNotifier changes, Widget rebuilds.<topic_end><topic_start>Networking<topic_end><topic_start>Cross-platform http networkingThe http package provides the simplest way to issue http requests. Thispackage is supported on Android, iOS, macOS, Windows, Linux and the web.<topic_end><topic_start>Platform notesSome platforms require additional steps, as detailed below.<topic_end><topic_start>AndroidAndroid apps must declare their use of the internet in the Androidmanifest (AndroidManifest.xml):<topic_end><topic_start>macOSmacOS apps must allow network access in the relevant *.entitlements files.Learn more about setting up entitlements.<topic_end><topic_start>SamplesFor a practical sample of various networking tasks (incl. fetching data,WebSockets, and parsing data in the background) see the networking cookbook.<topic_end><topic_start>Fetch data from the internetFetching data from the internet is necessary for most apps.Luckily, Dart and Flutter provide tools, such as thehttp package, for this type of work.info Note  You should avoid directly using dart:io or dart:html  to make HTTP requests.  Those libraries are platform-dependent  and tied to a single implementation.This recipe uses the following steps:<topic_end><topic_start>1. Add the http packageThe http package provides thesimplest way to fetch data from the internet.To add the http package as a dependency,run flutter pub add:Import the http package.<code_start>import 'package:http/http.dart' as http;<code_end>If you are deploying to Android, edit your AndroidManifest.xml file to add the Internet permission.Likewise, if you are deploying to macOS, edit your macos/Runner/DebugProfile.entitlements and macos/Runner/Release.entitlementsfiles to include the network client entitlement.<topic_end><topic_start>2. Make a network requestThis recipe covers how to fetch a sample album from theJSONPlaceholder using the http.get() method.<code_start>Future<http.Response> fetchAlbum() {  return http.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));}<code_end>The http.get() method returns a Future that contains a Response.<topic_end><topic_start>3. Convert the response into a custom Dart objectWhile it’s easy to make a network request, working with a rawFuture<http.Response> isn’t very convenient.To make your life easier,convert the http.Response into a Dart object.<topic_end><topic_start>Create an Album classFirst, create an Album class that contains the data from thenetwork request. It includes a factory constructor thatcreates an Album from JSON.Converting JSON using pattern matching is only one option.For more information, see the full article onJSON and serialization.<code_start>class Album {  final int userId;  final int id;  final String title;  const Album({    required this.userId,    required this.id,    required this.title,  });  factory Album.fromJson(Map<String, dynamic> json) {    return switch (json) {      {        'userId': int userId,        'id': int id,        'title': String title,      } =>        Album(          userId: userId,          id: id,          title: title,        ),      _ => throw const FormatException('Failed to load album.'),    };  }}<code_end><topic_end><topic_start>Convert the http.Response to an AlbumNow, use the following steps to update the fetchAlbum()function to return a Future<Album>:<code_start>Future<Album> fetchAlbum() async {  final response = await http      .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));  if (response.statusCode == 200) {    // If the server did return a 200 OK response,    // then parse the JSON.    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);  } else {    // If the server did not return a 200 OK response,    // then throw an exception.    throw Exception('Failed to load album');  }}<code_end>Hooray!Now you’ve got a function that fetches an album from the internet.<topic_end><topic_start>4. Fetch the dataCall the fetchAlbum() method in either theinitState() or didChangeDependencies()methods.The initState() method is called exactly once and then never again.If you want to have the option of reloading the API in response to anInheritedWidget changing, put the call into thedidChangeDependencies() method.See State for more details.<code_start>class _MyAppState extends State<MyApp> {  late Future<Album> futureAlbum;  @override  void initState() {    super.initState();    futureAlbum = fetchAlbum();  }  // ···}<code_end>This Future is used in the next step.<topic_end><topic_start>5. Display the dataTo display the data on screen, use theFutureBuilder widget.The FutureBuilder widget comes with Flutter andmakes it easy to work with asynchronous data sources.You must provide two parameters:Note that snapshot.hasData only returns truewhen the snapshot contains a non-null data value.Because fetchAlbum can only return non-null values,the function should throw an exceptioneven in the case of a “404 Not Found” server response.Throwing an exception sets the snapshot.hasError to truewhich can be used to display an error message.Otherwise, the spinner will be displayed.<code_start>FutureBuilder<Album>(  future: futureAlbum,  builder: (context, snapshot) {    if (snapshot.hasData) {      return Text(snapshot.data!.title);    } else if (snapshot.hasError) {      return Text('${snapshot.error}');    }    // By default, show a loading spinner.    return const CircularProgressIndicator();  },)<code_end><topic_end><topic_start>Why is fetchAlbum() called in initState()?Although it’s convenient,it’s not recommended to put an API call in a build() method.Flutter calls the build() method every time it needsto change anything in the view,and this happens surprisingly often.The fetchAlbum() method, if placed inside build(), is repeatedly called on each rebuild causing the app to slow down.Storing the fetchAlbum() result in a state variable ensures thatthe Future is executed only once and then cached for subsequentrebuilds.<topic_end><topic_start>TestingFor information on how to test this functionality,see the following recipes:<topic_end><topic_start>Complete example<code_start>import 'dart:async';import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;Future<Album> fetchAlbum() async {  final response = await http      .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));  if (response.statusCode == 200) {    // If the server did return a 200 OK response,    // then parse the JSON.    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);  } else {    // If the server did not return a 200 OK response,    // then throw an exception.    throw Exception('Failed to load album');  }}class Album {  final int userId;  final int id;  final String title;  const Album({    required this.userId,    required this.id,    required this.title,  });  factory Album.fromJson(Map<String, dynamic> json) {    return switch (json) {      {        'userId': int userId,        'id': int id,        'title': String title,      } =>        Album(          userId: userId,          id: id,          title: title,        ),      _ => throw const FormatException('Failed to load album.'),    };  }}void main() => runApp(const MyApp());class MyApp extends StatefulWidget {  const MyApp({super.key});  @override  State<MyApp> createState() => _MyAppState();}class _MyAppState extends State<MyApp> {  late Future<Album> futureAlbum;  @override  void initState() {    super.initState();    futureAlbum = fetchAlbum();  }  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Fetch Data Example',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: Scaffold(        appBar: AppBar(          title: const Text('Fetch Data Example'),        ),        body: Center(          child: FutureBuilder<Album>(            future: futureAlbum,            builder: (context, snapshot) {              if (snapshot.hasData) {                return Text(snapshot.data!.title);              } else if (snapshot.hasError) {                return Text('${snapshot.error}');              }              // By default, show a loading spinner.              return const CircularProgressIndicator();            },          ),        ),      ),    );  }}<code_end><topic_end><topic_start>Make authenticated requestsTo fetch data from most web services, you need to provideauthorization. There are many ways to do this,but perhaps the most common uses the Authorization HTTP header.<topic_end><topic_start>Add authorization headersThe http package provides aconvenient way to add headers to your requests.Alternatively, use the HttpHeadersclass from the dart:io library.<code_start>final response = await http.get(  Uri.parse('https://jsonplaceholder.typicode.com/albums/1'),  // Send authorization headers to the backend.  headers: {    HttpHeaders.authorizationHeader: 'Basic your_api_token_here',  },);<code_end><topic_end><topic_start>Complete exampleThis example builds upon theFetching data from the internet recipe.<code_start>import 'dart:async';import 'dart:convert';import 'dart:io';import 'package:http/http.dart' as http;Future<Album> fetchAlbum() async {  final response = await http.get(    Uri.parse('https://jsonplaceholder.typicode.com/albums/1'),    // Send authorization headers to the backend.    headers: {      HttpHeaders.authorizationHeader: 'Basic your_api_token_here',    },  );  final responseJson = jsonDecode(response.body) as Map<String, dynamic>;  return Album.fromJson(responseJson);}class Album {  final int userId;  final int id;  final String title;  const Album({    required this.userId,    required this.id,    required this.title,  });  factory Album.fromJson(Map<String, dynamic> json) {    return switch (json) {      {        'userId': int userId,        'id': int id,        'title': String title,      } =>        Album(          userId: userId,          id: id,          title: title,        ),      _ => throw const FormatException('Failed to load album.'),    };  }}<code_end><topic_end><topic_start>Send data to the internetSending data to the internet is necessary for most apps.The http package has got that covered, too.This recipe uses the following steps:<topic_end><topic_start>1. Add the http packageTo add the http package as a dependency,run flutter pub add:Import the http package.<code_start>import 'package:http/http.dart' as http;<code_end>If you develop for android, add the following permission inside the manifest tagin the AndroidManifest.xml file located at android/app/src/main.<topic_end><topic_start>2. Sending data to serverThis recipe covers how to create an Albumby sending an album title to theJSONPlaceholder using thehttp.post() method.Import dart:convert for access to jsonEncode to encode the data:<code_start>import 'dart:convert';<code_end>Use the http.post() method to send the encoded data:<code_start>Future<http.Response> createAlbum(String title) {  return http.post(    Uri.parse('https://jsonplaceholder.typicode.com/albums'),    headers: <String, String>{      'Content-Type': 'application/json; charset=UTF-8',    },    body: jsonEncode(<String, String>{      'title': title,    }),  );}<code_end>The http.post() method returns a Future that contains a Response.<topic_end><topic_start>3. Convert the http.Response to a custom Dart objectWhile it’s easy to make a network request,working with a raw Future<http.Response>isn’t very convenient.  To make your life easier,convert the http.Response into a Dart object.<topic_end><topic_start>Create an Album classFirst, create an Album class that containsthe data from the network request.It includes a factory constructor thatcreates an Album from JSON.Converting JSON with pattern matching is only one option.For more information, see the full article onJSON and serialization.<code_start>class Album {  final int id;  final String title;  const Album({required this.id, required this.title});  factory Album.fromJson(Map<String, dynamic> json) {    return switch (json) {      {        'id': int id,        'title': String title,      } =>        Album(          id: id,          title: title,        ),      _ => throw const FormatException('Failed to load album.'),    };  }}<code_end><topic_end><topic_start>Convert the http.Response to an AlbumUse the following steps to update the createAlbum()function to return a Future<Album>:<code_start>Future<Album> createAlbum(String title) async {  final response = await http.post(    Uri.parse('https://jsonplaceholder.typicode.com/albums'),    headers: <String, String>{      'Content-Type': 'application/json; charset=UTF-8',    },    body: jsonEncode(<String, String>{      'title': title,    }),  );  if (response.statusCode == 201) {    // If the server did return a 201 CREATED response,    // then parse the JSON.    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);  } else {    // If the server did not return a 201 CREATED response,    // then throw an exception.    throw Exception('Failed to create album.');  }}<code_end>Hooray! Now you’ve got a function that sends the title to aserver to create an album.<topic_end><topic_start>4. Get a title from user inputNext, create a TextField to enter a title anda ElevatedButton to send data to server.Also define a TextEditingController to read theuser input from a TextField.When the ElevatedButton is pressed, the _futureAlbumis set to the value returned by createAlbum() method.<code_start>Column(  mainAxisAlignment: MainAxisAlignment.center,  children: <Widget>[    TextField(      controller: _controller,      decoration: const InputDecoration(hintText: 'Enter Title'),    ),    ElevatedButton(      onPressed: () {        setState(() {          _futureAlbum = createAlbum(_controller.text);        });      },      child: const Text('Create Data'),    ),  ],)<code_end>On pressing the Create Data button, make the network request,which sends the data in the TextField to the serveras a POST request.The Future, _futureAlbum, is used in the next step.<topic_end><topic_start>5. Display the response on screenTo display the data on screen, use theFutureBuilder widget.The FutureBuilder widget comes with Flutter andmakes it easy to work with asynchronous data sources.You must provide two parameters:Note that snapshot.hasData only returns true whenthe snapshot contains a non-null data value.This is why the createAlbum() function should throw an exceptioneven in the case of a “404 Not Found” server response.If createAlbum() returns null, thenCircularProgressIndicator displays indefinitely.<code_start>FutureBuilder<Album>(  future: _futureAlbum,  builder: (context, snapshot) {    if (snapshot.hasData) {      return Text(snapshot.data!.title);    } else if (snapshot.hasError) {      return Text('${snapshot.error}');    }    return const CircularProgressIndicator();  },)<code_end><topic_end><topic_start>Complete example<code_start>import 'dart:async';import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;Future<Album> createAlbum(String title) async {  final response = await http.post(    Uri.parse('https://jsonplaceholder.typicode.com/albums'),    headers: <String, String>{      'Content-Type': 'application/json; charset=UTF-8',    },    body: jsonEncode(<String, String>{      'title': title,    }),  );  if (response.statusCode == 201) {    // If the server did return a 201 CREATED response,    // then parse the JSON.    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);  } else {    // If the server did not return a 201 CREATED response,    // then throw an exception.    throw Exception('Failed to create album.');  }}class Album {  final int id;  final String title;  const Album({required this.id, required this.title});  factory Album.fromJson(Map<String, dynamic> json) {    return switch (json) {      {        'id': int id,        'title': String title,      } =>        Album(          id: id,          title: title,        ),      _ => throw const FormatException('Failed to load album.'),    };  }}void main() {  runApp(const MyApp());}class MyApp extends StatefulWidget {  const MyApp({super.key});  @override  State<MyApp> createState() {    return _MyAppState();  }}class _MyAppState extends State<MyApp> {  final TextEditingController _controller = TextEditingController();  Future<Album>? _futureAlbum;  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Create Data Example',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: Scaffold(        appBar: AppBar(          title: const Text('Create Data Example'),        ),        body: Container(          alignment: Alignment.center,          padding: const EdgeInsets.all(8),          child: (_futureAlbum == null) ? buildColumn() : buildFutureBuilder(),        ),      ),    );  }  Column buildColumn() {    return Column(      mainAxisAlignment: MainAxisAlignment.center,      children: <Widget>[        TextField(          controller: _controller,          decoration: const InputDecoration(hintText: 'Enter Title'),        ),        ElevatedButton(          onPressed: () {            setState(() {              _futureAlbum = createAlbum(_controller.text);            });          },          child: const Text('Create Data'),        ),      ],    );  }  FutureBuilder<Album> buildFutureBuilder() {    return FutureBuilder<Album>(      future: _futureAlbum,      builder: (context, snapshot) {        if (snapshot.hasData) {          return Text(snapshot.data!.title);        } else if (snapshot.hasError) {          return Text('${snapshot.error}');        }        return const CircularProgressIndicator();      },    );  }}<code_end><topic_end><topic_start>Update data over the internetUpdating data over the internet is necessary for most apps.The http package has got that covered!This recipe uses the following steps:<topic_end><topic_start>1. Add the http packageTo add the http package as a dependency,run flutter pub add:Import the http package.<code_start>import 'package:http/http.dart' as http;<code_end><topic_end><topic_start>2. Updating data over the internet using the http packageThis recipe covers how to update an album title to theJSONPlaceholder using the http.put() method.<code_start>Future<http.Response> updateAlbum(String title) {  return http.put(    Uri.parse('https://jsonplaceholder.typicode.com/albums/1'),    headers: <String, String>{      'Content-Type': 'application/json; charset=UTF-8',    },    body: jsonEncode(<String, String>{      'title': title,    }),  );}<code_end>The http.put() method returns a Future that contains a Response.<topic_end><topic_start>3. Convert the http.Response to a custom Dart objectWhile it’s easy to make a network request,working with a raw Future<http.Response>isn’t very convenient. To make your life easier,convert the http.Response into a Dart object.<topic_end><topic_start>Create an Album classFirst, create an Album class that contains the data from thenetwork request. It includes a factory constructor thatcreates an Album from JSON.Converting JSON with pattern matching is only one option.For more information, see the full article onJSON and serialization.<code_start>class Album {  final int id;  final String title;  const Album({required this.id, required this.title});  factory Album.fromJson(Map<String, dynamic> json) {    return switch (json) {      {        'id': int id,        'title': String title,      } =>        Album(          id: id,          title: title,        ),      _ => throw const FormatException('Failed to load album.'),    };  }}<code_end><topic_end><topic_start>Convert the http.Response to an AlbumNow, use the following steps to update the updateAlbum()function to return a Future<Album>:<code_start>Future<Album> updateAlbum(String title) async {  final response = await http.put(    Uri.parse('https://jsonplaceholder.typicode.com/albums/1'),    headers: <String, String>{      'Content-Type': 'application/json; charset=UTF-8',    },    body: jsonEncode(<String, String>{      'title': title,    }),  );  if (response.statusCode == 200) {    // If the server did return a 200 OK response,    // then parse the JSON.    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);  } else {    // If the server did not return a 200 OK response,    // then throw an exception.    throw Exception('Failed to update album.');  }}<code_end>Hooray!Now you’ve got a function that updates the title of an album.<topic_end><topic_start>4. Get the data from the internetGet the data from internet before you can update it.For a complete example, see the Fetch data recipe.<code_start>Future<Album> fetchAlbum() async {  final response = await http.get(    Uri.parse('https://jsonplaceholder.typicode.com/albums/1'),  );  if (response.statusCode == 200) {    // If the server did return a 200 OK response,    // then parse the JSON.    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);  } else {    // If the server did not return a 200 OK response,    // then throw an exception.    throw Exception('Failed to load album');  }}<code_end>Ideally, you will use this method to set_futureAlbum during initState to fetchthe data from the internet.<topic_end><topic_start>5. Update the existing title from user inputCreate a TextField to enter a title and a ElevatedButtonto update the data on server.Also define a TextEditingController toread the user input from a TextField.When the ElevatedButton is pressed,the _futureAlbum is set to the value returned byupdateAlbum() method.<code_start>Column(  mainAxisAlignment: MainAxisAlignment.center,  children: <Widget>[    Padding(      padding: const EdgeInsets.all(8),      child: TextField(        controller: _controller,        decoration: const InputDecoration(hintText: 'Enter Title'),      ),    ),    ElevatedButton(      onPressed: () {        setState(() {          _futureAlbum = updateAlbum(_controller.text);        });      },      child: const Text('Update Data'),    ),  ],);<code_end>On pressing the Update Data button, a network requestsends the data in the TextField to the server as a PUT request.The _futureAlbum variable is used in the next step.<topic_end><topic_start>5. Display the response on screenTo display the data on screen, use theFutureBuilder widget.The FutureBuilder widget comes with Flutter andmakes it easy to work with async data sources.You must provide two parameters:Note that snapshot.hasData only returns true whenthe snapshot contains a non-null data value.This is why the updateAlbum function should throw an exceptioneven in the case of a “404 Not Found” server response.If updateAlbum returns null thenCircularProgressIndicator will display indefinitely.<code_start>FutureBuilder<Album>(  future: _futureAlbum,  builder: (context, snapshot) {    if (snapshot.hasData) {      return Text(snapshot.data!.title);    } else if (snapshot.hasError) {      return Text('${snapshot.error}');    }    return const CircularProgressIndicator();  },);<code_end><topic_end><topic_start>Complete example<code_start>import 'dart:async';import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;Future<Album> fetchAlbum() async {  final response = await http.get(    Uri.parse('https://jsonplaceholder.typicode.com/albums/1'),  );  if (response.statusCode == 200) {    // If the server did return a 200 OK response,    // then parse the JSON.    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);  } else {    // If the server did not return a 200 OK response,    // then throw an exception.    throw Exception('Failed to load album');  }}Future<Album> updateAlbum(String title) async {  final response = await http.put(    Uri.parse('https://jsonplaceholder.typicode.com/albums/1'),    headers: <String, String>{      'Content-Type': 'application/json; charset=UTF-8',    },    body: jsonEncode(<String, String>{      'title': title,    }),  );  if (response.statusCode == 200) {    // If the server did return a 200 OK response,    // then parse the JSON.    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);  } else {    // If the server did not return a 200 OK response,    // then throw an exception.    throw Exception('Failed to update album.');  }}class Album {  final int id;  final String title;  const Album({required this.id, required this.title});  factory Album.fromJson(Map<String, dynamic> json) {    return switch (json) {      {        'id': int id,        'title': String title,      } =>        Album(          id: id,          title: title,        ),      _ => throw const FormatException('Failed to load album.'),    };  }}void main() {  runApp(const MyApp());}class MyApp extends StatefulWidget {  const MyApp({super.key});  @override  State<MyApp> createState() {    return _MyAppState();  }}class _MyAppState extends State<MyApp> {  final TextEditingController _controller = TextEditingController();  late Future<Album> _futureAlbum;  @override  void initState() {    super.initState();    _futureAlbum = fetchAlbum();  }  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Update Data Example',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: Scaffold(        appBar: AppBar(          title: const Text('Update Data Example'),        ),        body: Container(          alignment: Alignment.center,          padding: const EdgeInsets.all(8),          child: FutureBuilder<Album>(            future: _futureAlbum,            builder: (context, snapshot) {              if (snapshot.connectionState == ConnectionState.done) {                if (snapshot.hasData) {                  return Column(                    mainAxisAlignment: MainAxisAlignment.center,                    children: <Widget>[                      Text(snapshot.data!.title),                      TextField(                        controller: _controller,                        decoration: const InputDecoration(                          hintText: 'Enter Title',                        ),                      ),                      ElevatedButton(                        onPressed: () {                          setState(() {                            _futureAlbum = updateAlbum(_controller.text);                          });                        },                        child: const Text('Update Data'),                      ),                    ],                  );                } else if (snapshot.hasError) {                  return Text('${snapshot.error}');                }              }              return const CircularProgressIndicator();            },          ),        ),      ),    );  }}<code_end><topic_end><topic_start>Delete data on the internetThis recipe covers how to delete data overthe internet using the http package.This recipe uses the following steps:<topic_end><topic_start>1. Add the http packageTo add the http package as a dependency,run flutter pub add:Import the http package.<code_start>import 'package:http/http.dart' as http;<code_end><topic_end><topic_start>2. Delete data on the serverThis recipe covers how to delete an album from theJSONPlaceholder using the http.delete() method.Note that this requires the id of the album thatyou want to delete. For this example,use something you already know, for example id = 1.<code_start>Future<http.Response> deleteAlbum(String id) async {  final http.Response response = await http.delete(    Uri.parse('https://jsonplaceholder.typicode.com/albums/$id'),    headers: <String, String>{      'Content-Type': 'application/json; charset=UTF-8',    },  );  return response;}<code_end>The http.delete() method returns a Future that contains a Response.<topic_end><topic_start>3. Update the screenIn order to check whether the data has been deleted or not,first fetch the data from the JSONPlaceholderusing the http.get() method, and display it in the screen.(See the Fetch Data recipe for a complete example.)You should now have a Delete Data button that,when pressed, calls the deleteAlbum() method.<code_start>Column(  mainAxisAlignment: MainAxisAlignment.center,  children: <Widget>[    Text(snapshot.data?.title ?? 'Deleted'),    ElevatedButton(      child: const Text('Delete Data'),      onPressed: () {        setState(() {          _futureAlbum =              deleteAlbum(snapshot.data!.id.toString());        });      },    ),  ],);<code_end>Now, when you click on the Delete Data button,the deleteAlbum() method is called and the idyou are passing is the id of the data that you retrievedfrom the internet. This means you are going to deletethe same data that you fetched from the internet.<topic_end><topic_start>Returning a response from the deleteAlbum() methodOnce the delete request has been made,you can return a response from the deleteAlbum()method to notify our screen that the data has been deleted.<code_start>Future<Album> deleteAlbum(String id) async {  final http.Response response = await http.delete(    Uri.parse('https://jsonplaceholder.typicode.com/albums/$id'),    headers: <String, String>{      'Content-Type': 'application/json; charset=UTF-8',    },  );  if (response.statusCode == 200) {    // If the server did return a 200 OK response,    // then parse the JSON. After deleting,    // you'll get an empty JSON `{}` response.    // Don't return `null`, otherwise `snapshot.hasData`    // will always return false on `FutureBuilder`.    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);  } else {    // If the server did not return a "200 OK response",    // then throw an exception.    throw Exception('Failed to delete album.');  }}<code_end>FutureBuilder() now rebuilds when it receives a response.Since the response won’t have any data in its bodyif the request was successful,the Album.fromJson() method creates an instance of theAlbum object with a default value (null in our case).This behavior can be altered in any way you wish.That’s all!Now you’ve got a function that deletes the data from the internet.<topic_end><topic_start>Complete example<code_start>import 'dart:async';import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;Future<Album> fetchAlbum() async {  final response = await http.get(    Uri.parse('https://jsonplaceholder.typicode.com/albums/1'),  );  if (response.statusCode == 200) {    // If the server did return a 200 OK response, then parse the JSON.    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);  } else {    // If the server did not return a 200 OK response, then throw an exception.    throw Exception('Failed to load album');  }}Future<Album> deleteAlbum(String id) async {  final http.Response response = await http.delete(    Uri.parse('https://jsonplaceholder.typicode.com/albums/$id'),    headers: <String, String>{      'Content-Type': 'application/json; charset=UTF-8',    },  );  if (response.statusCode == 200) {    // If the server did return a 200 OK response,    // then parse the JSON. After deleting,    // you'll get an empty JSON `{}` response.    // Don't return `null`, otherwise `snapshot.hasData`    // will always return false on `FutureBuilder`.    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);  } else {    // If the server did not return a "200 OK response",    // then throw an exception.    throw Exception('Failed to delete album.');  }}class Album {  final int id;  final String title;  const Album({required this.id, required this.title});  factory Album.fromJson(Map<String, dynamic> json) {    return switch (json) {      {        'id': int id,        'title': String title,      } =>        Album(          id: id,          title: title,        ),      _ => throw const FormatException('Failed to load album.'),    };  }}void main() {  runApp(const MyApp());}class MyApp extends StatefulWidget {  const MyApp({super.key});  @override  State<MyApp> createState() {    return _MyAppState();  }}class _MyAppState extends State<MyApp> {  late Future<Album> _futureAlbum;  @override  void initState() {    super.initState();    _futureAlbum = fetchAlbum();  }  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Delete Data Example',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: Scaffold(        appBar: AppBar(          title: const Text('Delete Data Example'),        ),        body: Center(          child: FutureBuilder<Album>(            future: _futureAlbum,            builder: (context, snapshot) {              // If the connection is done,              // check for response data or an error.              if (snapshot.connectionState == ConnectionState.done) {                if (snapshot.hasData) {                  return Column(                    mainAxisAlignment: MainAxisAlignment.center,                    children: <Widget>[                      Text(snapshot.data?.title ?? 'Deleted'),                      ElevatedButton(                        child: const Text('Delete Data'),                        onPressed: () {                          setState(() {                            _futureAlbum =                                deleteAlbum(snapshot.data!.id.toString());                          });                        },                      ),                    ],                  );                } else if (snapshot.hasError) {                  return Text('${snapshot.error}');                }              }              // By default, show a loading spinner.              return const CircularProgressIndicator();            },          ),        ),      ),    );  }}<code_end><topic_end><topic_start>Communicate with WebSocketsIn addition to normal HTTP requests,you can connect to servers using WebSockets.WebSockets allow for two-way communication with a serverwithout polling.In this example, connect to atest WebSocket server sponsored by Lob.com.The server sends back the same message you send to it.This recipe uses the following steps:<topic_end><topic_start>1. Connect to a WebSocket serverThe web_socket_channel package provides thetools you need to connect to a WebSocket server.The package provides a WebSocketChannelthat allows you to both listen for messagesfrom the server and push messages to the server.In Flutter, use the following line tocreate a WebSocketChannel that connects to a server:<code_start>final channel = WebSocketChannel.connect(  Uri.parse('wss://echo.websocket.events'),);<code_end><topic_end><topic_start>2. Listen for messages from the serverNow that you’ve established a connection,listen to messages from the server.After sending a message to the test server,it sends the same message back.In this example, use a StreamBuilderwidget to listen for new messages, and aText widget to display them.<code_start>StreamBuilder(  stream: channel.stream,  builder: (context, snapshot) {    return Text(snapshot.hasData ? '${snapshot.data}' : '');  },)<code_end><topic_end><topic_start>How this worksThe WebSocketChannel provides aStream of messages from the server.The Stream class is a fundamental part of the dart:async package.It provides a way to listen to async events from a data source.Unlike Future, which returns a single async response,the Stream class can deliver many events over time.The StreamBuilder widget connects to a Streamand asks Flutter to rebuild every time itreceives an event using the given builder() function.<topic_end><topic_start>3. Send data to the serverTo send data to the server,add() messages to the sink providedby the WebSocketChannel.<code_start>channel.sink.add('Hello!');<code_end><topic_end><topic_start>How this worksThe WebSocketChannel provides aStreamSink to push messages to the server.The StreamSink class provides a general way to add sync or asyncevents to a data source.<topic_end><topic_start>4. Close the WebSocket connectionAfter you’re done using the WebSocket, close the connection:<code_start>channel.sink.close();<code_end><topic_end><topic_start>Complete example<code_start>import 'package:flutter/material.dart';import 'package:web_socket_channel/web_socket_channel.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const title = 'WebSocket Demo';    return const MaterialApp(      title: title,      home: MyHomePage(        title: title,      ),    );  }}class MyHomePage extends StatefulWidget {  const MyHomePage({    super.key,    required this.title,  });  final String title;  @override  State<MyHomePage> createState() => _MyHomePageState();}class _MyHomePageState extends State<MyHomePage> {  final TextEditingController _controller = TextEditingController();  final _channel = WebSocketChannel.connect(    Uri.parse('wss://echo.websocket.events'),  );  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text(widget.title),      ),      body: Padding(        padding: const EdgeInsets.all(20),        child: Column(          crossAxisAlignment: CrossAxisAlignment.start,          children: [            Form(              child: TextFormField(                controller: _controller,                decoration: const InputDecoration(labelText: 'Send a message'),              ),            ),            const SizedBox(height: 24),            StreamBuilder(              stream: _channel.stream,              builder: (context, snapshot) {                return Text(snapshot.hasData ? '${snapshot.data}' : '');              },            )          ],        ),      ),      floatingActionButton: FloatingActionButton(        onPressed: _sendMessage,        tooltip: 'Send message',        child: const Icon(Icons.send),      ), // This trailing comma makes auto-formatting nicer for build methods.    );  }  void _sendMessage() {    if (_controller.text.isNotEmpty) {      _channel.sink.add(_controller.text);    }  }  @override  void dispose() {    _channel.sink.close();    _controller.dispose();    super.dispose();  }}<code_end><topic_end><topic_start>Serialization<topic_end><topic_start>Topics<topic_end><topic_start>JSON and serializationIt is hard to think of a mobile app that doesn’t need to communicate with aweb server or easily store structured data at some point. When makingnetwork-connected apps, the chances are that it needs to consume some good oldJSON, sooner or later.This guide looks into ways of using JSON with Flutter.It covers which JSON solution to use in different scenarios, and why.infoTerminology: Encoding and serialization are the same  thing—turning a data structure into a string.  Decoding and deserialization are the  opposite process—turning a string into a data structure.  However, serialization also commonly refers to the entire process of  translating data structures to and from a more easily readable format.To avoid confusion, this doc uses “serialization” when referring to the  overall process, and “encoding” and “decoding” when specifically  referring to those processes.<topic_end><topic_start>Which JSON serialization method is right for me?This article covers two general strategies for working with JSON:Different projects come with different complexities and use cases.For smaller proof-of-concept projects or quick prototypes,using code generators might be overkill.For apps with several JSON models with more complexity,encoding by hand can quickly become tedious, repetitive,and lend itself to many small errors.<topic_end><topic_start>Use manual serialization for smaller projectsManual JSON decoding refers to using the built-in JSON decoder indart:convert. It involves passing the raw JSON string to the jsonDecode()function, and then looking up the values you need in the resultingMap<String, dynamic>.It has no external dependencies or particular setup process,and it’s good for a quick proof of concept.Manual decoding does not perform well when your project becomes bigger.Writing decoding logic by hand can become hard to manage and error-prone.If you have a typo when accessing a nonexistent JSONfield, your code throws an error during runtime.If you do not have many JSON models in your project and arelooking to test a concept quickly,manual serialization might be the way you want to start.For an example of manual encoding, seeSerializing JSON manually using dart:convert.lightbulb Tip  For hands-on practice deserializing JSON and  taking advantage of Dart 3’s new features,  check out the Dive into Dart’s patterns and records codelab.<topic_end><topic_start>Use code generation for medium to large projectsJSON serialization with code generation means having an external librarygenerate the encoding boilerplate for you. After some initial setup,you run a file watcher that generates the code from your model classes.For example, json_serializable and built_value are thesekinds of libraries.This approach scales well for a larger project. No hand-writtenboilerplate is needed, and typos when accessing JSON fields are caught atcompile-time. The downside with code generation is that it requires someinitial setup. Also, the generated source files might produce visual clutterin your project navigator.You might want to use generated code for JSON serialization when you have amedium or a larger project. To see an example of code generation based JSONencoding, see Serializing JSON using code generation libraries.<topic_end><topic_start>Is there a GSON/Jackson/Moshi equivalent in Flutter?The simple answer is no.Such a library would require using runtime reflection, which is disabled inFlutter. Runtime reflection interferes with tree shaking, which Dart hassupported for quite a long time. With tree shaking, you can “shake off” unusedcode from your release builds. This optimizes the app’s size significantly.Since reflection makes all code implicitly used by default, it makes treeshaking difficult. The tools cannot know what parts are unused at runtime, sothe redundant code is hard to strip away. App sizes cannot be easily optimizedwhen using reflection.Although you cannot use runtime reflection with Flutter,some libraries give you similarly easy-to-use APIs but arebased on code generation instead. Thisapproach is covered in more detail in thecode generation libraries section.<topic_end><topic_start>Serializing JSON manually using dart:convertBasic JSON serialization in Flutter is very simple. Flutter has a built-indart:convert library that includes a straightforward JSON encoder anddecoder.The following sample JSON implements a simple user model.<code_start>{  "name": "John Smith",  "email": "john@example.com"}<code_end>With dart:convert,you can serialize this JSON model in two ways.<topic_end><topic_start>Serializing JSON inlineBy looking at the dart:convert documentation,you’ll see that you can decode the JSON by calling thejsonDecode() function, with the JSON string as the method argument.<code_start>final user = jsonDecode(jsonString) as Map<String, dynamic>;print('Howdy, ${user['name']}!');print('We sent the verification link to ${user['email']}.');<code_end>Unfortunately, jsonDecode() returns a dynamic, meaningthat you do not know the types of the values until runtime. With this approach,you lose most of the statically typed language features: type safety,autocompletion and most importantly, compile-time exceptions. Your code willbecome instantly more error-prone.For example, whenever you access the name or email fields, you could quicklyintroduce a typo. A typo that the compiler doesn’t know about since theJSON lives in a map structure.<topic_end><topic_start>Serializing JSON inside model classesCombat the previously mentioned problems by introducing a plain modelclass, called User in this example. Inside the User class, you’ll find:With this approach, the calling code can have type safety,autocompletion for the name and email fields, and compile-time exceptions.If you make typos or treat the fields as ints instead of Strings,the app won’t compile, instead of crashing at runtime.user.dart<code_start>class User {  final String name;  final String email;  User(this.name, this.email);  User.fromJson(Map<String, dynamic> json)      : name = json['name'] as String,        email = json['email'] as String;  Map<String, dynamic> toJson() => {        'name': name,        'email': email,      };}<code_end>The responsibility of the decoding logic is now moved inside the modelitself. With this new approach, you can decode a user easily.<code_start>final userMap = jsonDecode(jsonString) as Map<String, dynamic>;final user = User.fromJson(userMap);print('Howdy, ${user.name}!');print('We sent the verification link to ${user.email}.');<code_end>To encode a user, pass the User object to the jsonEncode() function.You don’t need to call the toJson() method, since jsonEncode()already does it for you.<code_start>String json = jsonEncode(user);<code_end>With this approach, the calling code doesn’t have to worry about JSONserialization at all. However, the model class still definitely has to.In a production app, you would want to ensure that the serializationworks properly. In practice, the User.fromJson() and User.toJson()methods both need to have unit tests in place to verify correct behavior.info  The cookbook contains a more comprehensive worked example of using  JSON model classes, using an isolate to parse  the JSON file on a background thread. This approach is ideal if you  need your app to remain responsive while the JSON file is being  decoded.However, real-world scenarios are not always that simple.Sometimes JSON API responses are more complex, for example since they contain nested JSON objects that must be parsed through their own modelclass.It would be nice if there were something that handled the JSON encodingand decoding for you.  Luckily, there is!<topic_end><topic_start>Serializing JSON using code generation librariesAlthough there are other libraries available, this guide usesjson_serializable, an automated source code generator thatgenerates the JSON serialization boilerplate for you.infoChoosing a library:  You might have noticed two Flutter Favorite packages  on pub.dev that generate JSON serialization code,  json_serializable and built_value.  How do you choose between these packages?  The json_serializable package allows you to make regular  classes serializable by using annotations,   whereas the built_value package provides a higher-level way  of defining immutable value classes that can also be  serialized to JSON.Since the serialization code is not handwritten or maintained manuallyanymore, you minimize the risk of having JSON serialization exceptions atruntime.<topic_end><topic_start>Setting up json_serializable in a projectTo include json_serializable in your project, you need one regulardependency, and two dev dependencies. In short, dev dependenciesare dependencies that are not included in our app source code—theyare only used in the development environment.To add the dependencies, run flutter pub add:Run flutter pub get inside your project root folder(or click Packages get in your editor)to make these new dependencies available in your project.<topic_end><topic_start>Creating model classes the json_serializable wayThe following shows how to convert the User class to ajson_serializable class. For the sake of simplicity,this code uses the simplified JSON modelfrom the previous samples.user.dart<code_start>import 'package:json_annotation/json_annotation.dart';/// This allows the `User` class to access private members in/// the generated file. The value for this is *.g.dart, where/// the star denotes the source file name.part 'user.g.dart';/// An annotation for the code generator to know that this class needs the/// JSON serialization logic to be generated.@JsonSerializable()class User {  User(this.name, this.email);  String name;  String email;  /// A necessary factory constructor for creating a new User instance  /// from a map. Pass the map to the generated `_$UserFromJson()` constructor.  /// The constructor is named after the source class, in this case, User.  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);  /// `toJson` is the convention for a class to declare support for serialization  /// to JSON. The implementation simply calls the private, generated  /// helper method `_$UserToJson`.  Map<String, dynamic> toJson() => _$UserToJson(this);}<code_end>With this setup, the source code generator generates code for encodingand decoding the name and email fields from JSON.If needed, it is also easy to customize the naming strategy.For example, if the API returns objects with snake_case,and you want to use lowerCamelCase in your models,you can use the @JsonKey annotation with a name parameter:It’s best if both server and client follow the same naming strategy.@JsonSerializable() provides fieldRename enum for totally converting dart fields into JSON keys.Modifying @JsonSerializable(fieldRename: FieldRename.snake) is equivalent toadding @JsonKey(name: '<snake_case>') to each field.Sometimes server data is uncertain, so it is necessary to verify and protect data on client.Other commonly used @JsonKey annotations include:<topic_end><topic_start>Running the code generation utilityWhen creating json_serializable classes the first time,you’ll get errors similar to what is shown in the image below.These errors are entirely normal and are simply because the generated code forthe model class does not exist yet. To resolve this, run the codegenerator that generates the serialization boilerplate.There are two ways of running the code generator.<topic_end><topic_start>One-time code generationBy running dart run build_runner build --delete-conflicting-outputs in the project root,you generate JSON serialization code for your models whenever they are needed.This triggers a one-time build that goes through the source files, picks therelevant ones, and generates the necessary serialization code for them.While this is convenient, it would be nice if you did not have to run thebuild manually every time you make changes in your model classes.<topic_end><topic_start>Generating code continuouslyA watcher makes our source code generation process more convenient. Itwatches changes in our project files and automatically builds the necessaryfiles when needed. Start the watcher by runningdart run build_runner watch --delete-conflicting-outputs in the project root.It is safe to start the watcher once and leave it running in the background.<topic_end><topic_start>Consuming json_serializable modelsTo decode a JSON string the json_serializable way,you do not have actually to make any changes to our previous code.<code_start>final userMap = jsonDecode(jsonString) as Map<String, dynamic>;final user = User.fromJson(userMap);<code_end>The same goes for encoding. The calling API is the same as before.<code_start>String json = jsonEncode(user);<code_end>With json_serializable,you can forget any manual JSON serialization in the User class.The source code generator creates a file called user.g.dart,that has all the necessary serialization logic.You no longer have to write automated tests to ensurethat the serialization works—it’s nowthe library’s responsibility to make sure the serialization worksappropriately.<topic_end><topic_start>Generating code for nested classesYou might have code that has nested classes within a class.If that is the case, and you have tried to pass the class in JSON formatas an argument to a service (such as Firebase, for example),you might have experienced an Invalid argument error.Consider the following Address class:<code_start>import 'package:json_annotation/json_annotation.dart';part 'address.g.dart';@JsonSerializable()class Address {  String street;  String city;  Address(this.street, this.city);  factory Address.fromJson(Map<String, dynamic> json) =>      _$AddressFromJson(json);  Map<String, dynamic> toJson() => _$AddressToJson(this);}<code_end>The Address class is nested inside the User class:<code_start>import 'package:json_annotation/json_annotation.dart';import 'address.dart';part 'user.g.dart';@JsonSerializable()class User {  User(this.name, this.address);  String name;  Address address;  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);  Map<String, dynamic> toJson() => _$UserToJson(this);}<code_end>Running dart run build_runner build --delete-conflicting-outputsin the terminal createsthe *.g.dart file, but the private _$UserToJson() functionlooks something like the following:All looks fine now, but if you do a print() on the user object:<code_start>Address address = Address('My st.', 'New York');User user = User('John', address);print(user.toJson());<code_end>The result is:When what you probably want is output like the following:To make this work, pass explicitToJson: true in the @JsonSerializable()annotation over the class declaration. The User class now looks as follows:<code_start>import 'package:json_annotation/json_annotation.dart';import 'address.dart';part 'user.g.dart';@JsonSerializable(explicitToJson: true)class User {  User(this.name, this.address);  String name;  Address address;  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);  Map<String, dynamic> toJson() => _$UserToJson(this);}<code_end>For more information, see explicitToJson in theJsonSerializable class for the json_annotation package.<topic_end><topic_start>Further referencesFor more information, see the following resources:<topic_end><topic_start>Parse JSON in the backgroundBy default, Dart apps do all of their work on a single thread.In many cases, this model simplifies coding and is fast enoughthat it does not result in poor app performance or stuttering animations,often called “jank.”However, you might need to perform an expensive computation,such as parsing a very large JSON document.If this work takes more than 16 milliseconds,your users experience jank.To avoid jank, you need to perform expensive computationslike this in the background.On Android, this means scheduling work on a different thread.In Flutter, you can use a separate Isolate.This recipe uses the following steps:<topic_end><topic_start>1. Add the http packageFirst, add the http package to your project.The http package makes it easier to perform networkrequests, such as fetching data from a JSON endpoint.To add the http package as a dependency,run flutter pub add:<topic_end><topic_start>2. Make a network requestThis example covers how to fetch a large JSON documentthat contains a list of 5000 photo objects from theJSONPlaceholder REST API,using the http.get() method.<code_start>Future<http.Response> fetchPhotos(http.Client client) async {  return client.get(Uri.parse('https://jsonplaceholder.typicode.com/photos'));}<code_end>info Note  You’re providing an http.Client to the function in this example.  This makes the function easier to test and use in different environments.<topic_end><topic_start>3. Parse and convert the JSON into a list of photosNext, following the guidance from theFetch data from the internet recipe,convert the http.Response into a list of Dart objects.This makes the data easier to work with.<topic_end><topic_start>Create a Photo classFirst, create a Photo class that contains data about a photo.Include a fromJson() factory method to make it easy to create aPhoto starting with a JSON object.<code_start>class Photo {  final int albumId;  final int id;  final String title;  final String url;  final String thumbnailUrl;  const Photo({    required this.albumId,    required this.id,    required this.title,    required this.url,    required this.thumbnailUrl,  });  factory Photo.fromJson(Map<String, dynamic> json) {    return Photo(      albumId: json['albumId'] as int,      id: json['id'] as int,      title: json['title'] as String,      url: json['url'] as String,      thumbnailUrl: json['thumbnailUrl'] as String,    );  }}<code_end><topic_end><topic_start>Convert the response into a list of photosNow, use the following instructions to update thefetchPhotos() function so that it returns aFuture<List<Photo>>:<code_start>// A function that converts a response body into a List<Photo>.List<Photo> parsePhotos(String responseBody) {  final parsed =      (jsonDecode(responseBody) as List).cast<Map<String, dynamic>>();  return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();}Future<List<Photo>> fetchPhotos(http.Client client) async {  final response = await client      .get(Uri.parse('https://jsonplaceholder.typicode.com/photos'));  // Synchronously run parsePhotos in the main isolate.  return parsePhotos(response.body);}<code_end><topic_end><topic_start>4. Move this work to a separate isolateIf you run the fetchPhotos() function on a slower device,you might notice the app freezes for a brief moment as it parses andconverts the JSON. This is jank, and you want to get rid of it.You can remove the jank by moving the parsing and conversionto a background isolate using the compute()function provided by Flutter. The compute() function runs expensivefunctions in a background isolate and returns the result. In this case,run the parsePhotos() function in the background.<code_start>Future<List<Photo>> fetchPhotos(http.Client client) async {  final response = await client      .get(Uri.parse('https://jsonplaceholder.typicode.com/photos'));  // Use the compute function to run parsePhotos in a separate isolate.  return compute(parsePhotos, response.body);}<code_end><topic_end><topic_start>Notes on working with isolatesIsolates communicate by passing messages back and forth. These messages canbe primitive values, such as null, num, bool, double, or String, orsimple objects such as the List<Photo> in this example.You might experience errors if you try to pass more complex objects,such as a Future or http.Response between isolates.As an alternate solution, check out the worker_manager orworkmanager packages for background processing.<topic_end><topic_start>Complete example<code_start>import 'dart:async';import 'dart:convert';import 'package:flutter/foundation.dart';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;Future<List<Photo>> fetchPhotos(http.Client client) async {  final response = await client      .get(Uri.parse('https://jsonplaceholder.typicode.com/photos'));  // Use the compute function to run parsePhotos in a separate isolate.  return compute(parsePhotos, response.body);}// A function that converts a response body into a List<Photo>.List<Photo> parsePhotos(String responseBody) {  final parsed =      (jsonDecode(responseBody) as List).cast<Map<String, dynamic>>();  return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();}class Photo {  final int albumId;  final int id;  final String title;  final String url;  final String thumbnailUrl;  const Photo({    required this.albumId,    required this.id,    required this.title,    required this.url,    required this.thumbnailUrl,  });  factory Photo.fromJson(Map<String, dynamic> json) {    return Photo(      albumId: json['albumId'] as int,      id: json['id'] as int,      title: json['title'] as String,      url: json['url'] as String,      thumbnailUrl: json['thumbnailUrl'] as String,    );  }}void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    const appTitle = 'Isolate Demo';    return const MaterialApp(      title: appTitle,      home: MyHomePage(title: appTitle),    );  }}class MyHomePage extends StatelessWidget {  const MyHomePage({super.key, required this.title});  final String title;  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text(title),      ),      body: FutureBuilder<List<Photo>>(        future: fetchPhotos(http.Client()),        builder: (context, snapshot) {          if (snapshot.hasError) {            return const Center(              child: Text('An error has occurred!'),            );          } else if (snapshot.hasData) {            return PhotosList(photos: snapshot.data!);          } else {            return const Center(              child: CircularProgressIndicator(),            );          }        },      ),    );  }}class PhotosList extends StatelessWidget {  const PhotosList({super.key, required this.photos});  final List<Photo> photos;  @override  Widget build(BuildContext context) {    return GridView.builder(      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(        crossAxisCount: 2,      ),      itemCount: photos.length,      itemBuilder: (context, index) {        return Image.network(photos[index].thumbnailUrl);      },    );  }}<code_end><topic_end><topic_start>Persistence<topic_end><topic_start>Topics<topic_end><topic_start>Store key-value data on diskIf you have a relatively small collection of key-valuesto save, you can use the shared_preferences plugin.Normally, you would have towrite native platform integrations for storing data on each platform.Fortunately, the shared_preferences plugin can be used topersist key-value data to disk on each platform Flutter supports.This recipe uses the following steps:info Note  To learn more, watch this short Package of the Week video  on the shared_preferences package:<topic_end><topic_start>1. Add the dependencyBefore starting, add the shared_preferences package as a dependency.To add the shared_preferences package as a dependency,run flutter pub add:<topic_end><topic_start>2. Save dataTo persist data, use the setter methods provided by theSharedPreferences class. Setter methods are available forvarious primitive types, such as setInt, setBool, and setString.Setter methods do two things: First, synchronously update thekey-value pair in memory. Then, persist the data to disk.<code_start>// Load and obtain the shared preferences for this app.final prefs = await SharedPreferences.getInstance();// Save the counter value to persistent storage under the 'counter' key.await prefs.setInt('counter', counter);<code_end><topic_end><topic_start>3. Read dataTo read data, use the appropriate getter method provided by theSharedPreferences class. For each setter there is a corresponding getter.For example, you can use the getInt, getBool, and getString methods.<code_start>final prefs = await SharedPreferences.getInstance();// Try reading the counter value from persistent storage.// If not present, null is returned, so default to 0.final counter = prefs.getInt('counter') ?? 0;<code_end>Note that the getter methods throw an exception if the persisted valuehas a different type than the getter method expects.<topic_end><topic_start>4. Remove dataTo delete data, use the remove() method.<code_start>final prefs = await SharedPreferences.getInstance();// Remove the counter key-value pair from persistent storage.await prefs.remove('counter');<code_end><topic_end><topic_start>Supported typesAlthough the key-value storage provided by shared_preferences iseasy and convenient to use, it has limitations:<topic_end><topic_start>Testing supportIt’s a good idea to test code that persists data using shared_preferences.To enable this, the package provides anin-memory mock implementation of the preference store.To set up your tests to use the mock implementation,call the setMockInitialValues static method ina setUpAll() method in your test files.Pass in a map of key-value pairs to use as the initial values.<code_start>SharedPreferences.setMockInitialValues(<String, Object>{  'counter': 2,});<code_end><topic_end><topic_start>Complete example<code_start>import 'package:flutter/material.dart';import 'package:shared_preferences/shared_preferences.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Shared preferences demo',      home: MyHomePage(title: 'Shared preferences demo'),    );  }}class MyHomePage extends StatefulWidget {  const MyHomePage({super.key, required this.title});  final String title;  @override  State<MyHomePage> createState() => _MyHomePageState();}class _MyHomePageState extends State<MyHomePage> {  int _counter = 0;  @override  void initState() {    super.initState();    _loadCounter();  }  /// Load the initial counter value from persistent storage on start,  /// or fallback to 0 if it doesn't exist.  Future<void> _loadCounter() async {    final prefs = await SharedPreferences.getInstance();    setState(() {      _counter = prefs.getInt('counter') ?? 0;    });  }  /// After a click, increment the counter state and  /// asynchronously save it to persistent storage.  Future<void> _incrementCounter() async {    final prefs = await SharedPreferences.getInstance();    setState(() {      _counter = (prefs.getInt('counter') ?? 0) + 1;      prefs.setInt('counter', _counter);    });  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text(widget.title),      ),      body: Center(        child: Column(          mainAxisAlignment: MainAxisAlignment.center,          children: [            const Text(              'You have pushed the button this many times: ',            ),            Text(              '$_counter',              style: Theme.of(context).textTheme.headlineMedium,            ),          ],        ),      ),      floatingActionButton: FloatingActionButton(        onPressed: _incrementCounter,        tooltip: 'Increment',        child: const Icon(Icons.add),      ),    );  }}<code_end><topic_end><topic_start>Read and write filesIn some cases, you need to read and write files to disk.For example, you might need to persist data across app launches,or download data from the internet and save it for later offline use.To save files to disk on mobile or desktop apps,combine the path_provider plugin with the dart:io library.This recipe uses the following steps:To learn more, watch this Package of the Week videoon the path_provider package:info Note  This recipe doesn’t work with web apps at this time.  To follow the discussion on this issue,  check out flutter/flutter issue #45296.<topic_end><topic_start>1. Find the correct local pathThis example displays a counter. When the counter changes,write data on disk so you can read it again when the app loads.Where should you store this data?The path_provider packageprovides a platform-agnostic way to access commonly used locations on thedevice’s file system. The plugin currently supports access totwo file system locations:This example stores information in the documents directory.You can find the path to the documents directory as follows:<code_start>import 'package:path_provider/path_provider.dart';// ···  Future<String> get _localPath async {    final directory = await getApplicationDocumentsDirectory();    return directory.path;  }<code_end><topic_end><topic_start>2. Create a reference to the file locationOnce you know where to store the file, create a reference to thefile’s full location. You can use the Fileclass from the dart:io library to achieve this.<code_start>Future<File> get _localFile async {  final path = await _localPath;  return File('$path/counter.txt');}<code_end><topic_end><topic_start>3. Write data to the fileNow that you have a File to work with,use it to read and write data.First, write some data to the file.The counter is an integer, but is written to thefile as a string using the '$counter' syntax.<code_start>Future<File> writeCounter(int counter) async {  final file = await _localFile;  // Write the file  return file.writeAsString('$counter');}<code_end><topic_end><topic_start>4. Read data from the fileNow that you have some data on disk, you can read it.Once again, use the File class.<code_start>Future<int> readCounter() async {  try {    final file = await _localFile;    // Read the file    final contents = await file.readAsString();    return int.parse(contents);  } catch (e) {    // If encountering an error, return 0    return 0;  }}<code_end><topic_end><topic_start>Complete example<code_start>import 'dart:async';import 'dart:io';import 'package:flutter/material.dart';import 'package:path_provider/path_provider.dart';void main() {  runApp(    MaterialApp(      title: 'Reading and Writing Files',      home: FlutterDemo(storage: CounterStorage()),    ),  );}class CounterStorage {  Future<String> get _localPath async {    final directory = await getApplicationDocumentsDirectory();    return directory.path;  }  Future<File> get _localFile async {    final path = await _localPath;    return File('$path/counter.txt');  }  Future<int> readCounter() async {    try {      final file = await _localFile;      // Read the file      final contents = await file.readAsString();      return int.parse(contents);    } catch (e) {      // If encountering an error, return 0      return 0;    }  }  Future<File> writeCounter(int counter) async {    final file = await _localFile;    // Write the file    return file.writeAsString('$counter');  }}class FlutterDemo extends StatefulWidget {  const FlutterDemo({super.key, required this.storage});  final CounterStorage storage;  @override  State<FlutterDemo> createState() => _FlutterDemoState();}class _FlutterDemoState extends State<FlutterDemo> {  int _counter = 0;  @override  void initState() {    super.initState();    widget.storage.readCounter().then((value) {      setState(() {        _counter = value;      });    });  }  Future<File> _incrementCounter() {    setState(() {      _counter++;    });    // Write the variable as a string to the file.    return widget.storage.writeCounter(_counter);  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('Reading and Writing Files'),      ),      body: Center(        child: Text(          'Button tapped $_counter time${_counter == 1 ? '' : 's'}.',        ),      ),      floatingActionButton: FloatingActionButton(        onPressed: _incrementCounter,        tooltip: 'Increment',        child: const Icon(Icons.add),      ),    );  }}<code_end><topic_end><topic_start>Persist data with SQLiteinfo Note  This guide uses the sqflite package.  This package only supports apps that run on  macOS, iOS, or Android.If you are writing an app that needs to persist and query large amounts of data onthe local device, consider using a database instead of a local file orkey-value store. In general, databases provide faster inserts, updates,and queries compared to other local persistence solutions.Flutter apps can make use of the SQLite databases via thesqflite plugin available on pub.dev.This recipe demonstrates the basics of using sqfliteto insert, read, update, and remove data about various Dogs.If you are new to SQLite and SQL statements, review theSQLite Tutorial to learn the basics beforecompleting this recipe.This recipe uses the following steps:<topic_end><topic_start>1. Add the dependenciesTo work with SQLite databases, import the sqflite andpath packages.To add the packages as a dependency,run flutter pub add:Make sure to import the packages in the file you’ll be working in.<code_start>import 'dart:async';import 'package:flutter/widgets.dart';import 'package:path/path.dart';import 'package:sqflite/sqflite.dart';<code_end><topic_end><topic_start>2. Define the Dog data modelBefore creating the table to store information on Dogs, take a few moments todefine the data that needs to be stored. For this example, define a Dog classthat contains three pieces of data:A unique id, the name, and the age of each dog.<code_start>class Dog {  final int id;  final String name;  final int age;  const Dog({    required this.id,    required this.name,    required this.age,  });}<code_end><topic_end><topic_start>3. Open the databaseBefore reading and writing data to the database, open a connectionto the database. This involves two steps:info Note  In order to use the keyword await, the code must be placed  inside an async function. You should place all the following  table functions inside void main() async {}.<code_start>// Avoid errors caused by flutter upgrade.// Importing 'package:flutter/widgets.dart' is required.WidgetsFlutterBinding.ensureInitialized();// Open the database and store the reference.final database = openDatabase(  // Set the path to the database. Note: Using the `join` function from the  // `path` package is best practice to ensure the path is correctly  // constructed for each platform.  join(await getDatabasesPath(), 'doggie_database.db'),);<code_end><topic_end><topic_start>4. Create the dogs tableNext, create a table to store information about various Dogs.For this example, create a table called dogs that defines the datathat can be stored. Each Dog contains an id, name, and age.Therefore, these are represented as three columns in the dogs table.For more information about the available Datatypes that can be stored in aSQLite database, see the official SQLite Datatypes documentation.<code_start>final database = openDatabase(  // Set the path to the database. Note: Using the `join` function from the  // `path` package is best practice to ensure the path is correctly  // constructed for each platform.  join(await getDatabasesPath(), 'doggie_database.db'),  // When the database is first created, create a table to store dogs.  onCreate: (db, version) {    // Run the CREATE TABLE statement on the database.    return db.execute(      'CREATE TABLE dogs(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)',    );  },  // Set the version. This executes the onCreate function and provides a  // path to perform database upgrades and downgrades.  version: 1,);<code_end><topic_end><topic_start>5. Insert a Dog into the databaseNow that you have a database with a table suitable for storing informationabout various dogs, it’s time to read and write data.First, insert a Dog into the dogs table. This involves two steps:<code_start>class Dog {  final int id;  final String name;  final int age;  Dog({    required this.id,    required this.name,    required this.age,  });  // Convert a Dog into a Map. The keys must correspond to the names of the  // columns in the database.  Map<String, Object?> toMap() {    return {      'id': id,      'name': name,      'age': age,    };  }  // Implement toString to make it easier to see information about  // each dog when using the print statement.  @override  String toString() {    return 'Dog{id: $id, name: $name, age: $age}';  }}<code_end><code_start>// Define a function that inserts dogs into the databaseFuture<void> insertDog(Dog dog) async {  // Get a reference to the database.  final db = await database;  // Insert the Dog into the correct table. You might also specify the  // `conflictAlgorithm` to use in case the same dog is inserted twice.  //  // In this case, replace any previous data.  await db.insert(    'dogs',    dog.toMap(),    conflictAlgorithm: ConflictAlgorithm.replace,  );}<code_end><code_start>// Create a Dog and add it to the dogs tablevar fido = Dog(  id: 0,  name: 'Fido',  age: 35,);await insertDog(fido);<code_end><topic_end><topic_start>6. Retrieve the list of DogsNow that a Dog is stored in the database, query the databasefor a specific dog or a list of all dogs. This involves two steps:<code_start>// A method that retrieves all the dogs from the dogs table.Future<List<Dog>> dogs() async {  // Get a reference to the database.  final db = await database;  // Query the table for all the dogs.  final List<Map<String, Object?>> dogMaps = await db.query('dogs');  // Convert the list of each dog's fields into a list of `Dog` objects.  return [    for (final {          'id': id as int,          'name': name as String,          'age': age as int,        } in dogMaps)      Dog(id: id, name: name, age: age),  ];}<code_end><code_start>// Now, use the method above to retrieve all the dogs.print(await dogs()); // Prints a list that include Fido.<code_end><topic_end><topic_start>7. Update a Dog in the databaseAfter inserting information into the database,you might want to update that information at a later time.You can do this by using the update()method from the sqflite library.This involves two steps:<code_start>Future<void> updateDog(Dog dog) async {  // Get a reference to the database.  final db = await database;  // Update the given Dog.  await db.update(    'dogs',    dog.toMap(),    // Ensure that the Dog has a matching id.    where: 'id = ?',    // Pass the Dog's id as a whereArg to prevent SQL injection.    whereArgs: [dog.id],  );}<code_end><code_start>// Update Fido's age and save it to the database.fido = Dog(  id: fido.id,  name: fido.name,  age: fido.age + 7,);await updateDog(fido);// Print the updated results.print(await dogs()); // Prints Fido with age 42.<code_end>warning Warning  Always use whereArgs to pass arguments to a where statement.  This helps safeguard against SQL injection attacks.Do not use string interpolation, such as where: "id = ${dog.id}"!<topic_end><topic_start>8. Delete a Dog from the databaseIn addition to inserting and updating information about Dogs,you can also remove dogs from the database. To delete data,use the delete() method from the sqflite library.In this section, create a function that takes an id and deletes the dog witha matching id from the database. To make this work, you must provide a whereclause to limit the records being deleted.<code_start>Future<void> deleteDog(int id) async {  // Get a reference to the database.  final db = await database;  // Remove the Dog from the database.  await db.delete(    'dogs',    // Use a `where` clause to delete a specific dog.    where: 'id = ?',    // Pass the Dog's id as a whereArg to prevent SQL injection.    whereArgs: [id],  );}<code_end><topic_end><topic_start>ExampleTo run the example:<code_start>import 'dart:async';import 'package:flutter/widgets.dart';import 'package:path/path.dart';import 'package:sqflite/sqflite.dart';void main() async {  // Avoid errors caused by flutter upgrade.  // Importing 'package:flutter/widgets.dart' is required.  WidgetsFlutterBinding.ensureInitialized();  // Open the database and store the reference.  final database = openDatabase(    // Set the path to the database. Note: Using the `join` function from the    // `path` package is best practice to ensure the path is correctly    // constructed for each platform.    join(await getDatabasesPath(), 'doggie_database.db'),    // When the database is first created, create a table to store dogs.    onCreate: (db, version) {      // Run the CREATE TABLE statement on the database.      return db.execute(        'CREATE TABLE dogs(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)',      );    },    // Set the version. This executes the onCreate function and provides a    // path to perform database upgrades and downgrades.    version: 1,  );  // Define a function that inserts dogs into the database  Future<void> insertDog(Dog dog) async {    // Get a reference to the database.    final db = await database;    // Insert the Dog into the correct table. You might also specify the    // `conflictAlgorithm` to use in case the same dog is inserted twice.    //    // In this case, replace any previous data.    await db.insert(      'dogs',      dog.toMap(),      conflictAlgorithm: ConflictAlgorithm.replace,    );  }  // A method that retrieves all the dogs from the dogs table.  Future<List<Dog>> dogs() async {    // Get a reference to the database.    final db = await database;    // Query the table for all the dogs.    final List<Map<String, Object?>> dogMaps = await db.query('dogs');    // Convert the list of each dog's fields into a list of `Dog` objects.    return [      for (final {            'id': id as int,            'name': name as String,            'age': age as int,          } in dogMaps)        Dog(id: id, name: name, age: age),    ];  }  Future<void> updateDog(Dog dog) async {    // Get a reference to the database.    final db = await database;    // Update the given Dog.    await db.update(      'dogs',      dog.toMap(),      // Ensure that the Dog has a matching id.      where: 'id = ?',      // Pass the Dog's id as a whereArg to prevent SQL injection.      whereArgs: [dog.id],    );  }  Future<void> deleteDog(int id) async {    // Get a reference to the database.    final db = await database;    // Remove the Dog from the database.    await db.delete(      'dogs',      // Use a `where` clause to delete a specific dog.      where: 'id = ?',      // Pass the Dog's id as a whereArg to prevent SQL injection.      whereArgs: [id],    );  }  // Create a Dog and add it to the dogs table  var fido = Dog(    id: 0,    name: 'Fido',    age: 35,  );  await insertDog(fido);  // Now, use the method above to retrieve all the dogs.  print(await dogs()); // Prints a list that include Fido.  // Update Fido's age and save it to the database.  fido = Dog(    id: fido.id,    name: fido.name,    age: fido.age + 7,  );  await updateDog(fido);  // Print the updated results.  print(await dogs()); // Prints Fido with age 42.  // Delete Fido from the database.  await deleteDog(fido.id);  // Print the list of dogs (empty).  print(await dogs());}class Dog {  final int id;  final String name;  final int age;  Dog({    required this.id,    required this.name,    required this.age,  });  // Convert a Dog into a Map. The keys must correspond to the names of the  // columns in the database.  Map<String, Object?> toMap() {    return {      'id': id,      'name': name,      'age': age,    };  }  // Implement toString to make it easier to see information about  // each dog when using the print statement.  @override  String toString() {    return 'Dog{id: $id, name: $name, age: $age}';  }}<code_end><topic_end><topic_start>Firebase<topic_end><topic_start>IntroductionFirebase is a Backend-as-a-Service (BaaS) app development platformthat provides hosted backend services such as a realtime database,cloud storage, authentication, crash reporting, machine learning,remote configuration, and hosting for your static files.<topic_end><topic_start>Flutter and Firebase resourcesFirebase supports Flutter. To learn more,check out the following resources.<topic_end><topic_start>Documentation<topic_end><topic_start>Blog PostsUse Firebase to host your Flutter app on the web<topic_end><topic_start>TutorialsGet to know Firebase for Flutter<topic_end><topic_start>Flutter and Firebase community resourcesThe Flutter community created the following useful resources.<topic_end><topic_start>Blog PostsBuilding chat app with Flutter and Firebase<topic_end><topic_start>Videos<topic_end><topic_start>Google APIsThe Google APIs package exposes dozens of Googleservices that you can use from Dart projects.This page describes how to use APIs that interact withend-user data by using Google authentication.Examples of user-data APIs includeCalendar, Gmail, YouTube, and Firebase.info   The only APIs you should use directly from your Flutter  project are those that access user data using Google authentication.APIs that require service accounts should not  be used directly from a Flutter application.  Doing so requires shipping service credentials as part  of your application, which is not secure.  To use these APIs,  we recommend creating an intermediate service.To add authentication to Firebase explicitly, check out theAdd a user authentication flow to a Flutter app using FirebaseUIcodelab and theGet Started with Firebase Authentication on Flutter docs.<topic_end><topic_start>OverviewTo use Google APIs, follow these steps:<topic_end><topic_start>1. Pick the desired APIThe documentation for package:googleapis listseach API as a separate Dart library&emdash;in aname_version format.Check out youtube_v3 as an example.Each library might provide many types,but there is one root class that ends in Api.For YouTube, it’s YouTubeApi.Not only is the Api class the one you need toinstantiate (see step 3), but it alsoexposes the scopes that represent the permissionsneeded to use the API. For example,the Constants section of theYouTubeApi class lists the available scopes.To request access to read (but not write) an end-usersYouTube data, authenticate the user withyoutubeReadonlyScope.<code_start>/// Provides the `YouTubeApi` class.import 'package:googleapis/youtube/v3.dart';<code_end><topic_end><topic_start>2. Enable the APITo use Google APIs you must have a Google accountand a Google project. You alsoneed to enable your desired API.This example enables YouTube Data API v3.For details, see the getting started instructions.<topic_end><topic_start>3. Authenticate the user with the required scopesUse the google_sign_in package toauthenticate users with their Google identity.Configure signin for each platform you want to support.<code_start>/// Provides the `GoogleSignIn` classimport 'package:google_sign_in/google_sign_in.dart';<code_end>When instantiating the GoogleSignIn class,provide the desired scopes as discussedin the previous section.<code_start>final _googleSignIn = GoogleSignIn(  scopes: <String>[YouTubeApi.youtubeReadonlyScope],);<code_end>Follow the instructions provided bypackage:google_sign_into allow a user to authenticate.Once authenticated,you must obtain an authenticated HTTP client.<topic_end><topic_start>4. Obtain an authenticated HTTP clientThe extension_google_sign_in_as_googleapis_authpackage provides an extension method on GoogleSignIncalled authenticatedClient.<code_start>import 'package:extension_google_sign_in_as_googleapis_auth/extension_google_sign_in_as_googleapis_auth.dart';<code_end>Add a listener to onCurrentUserChangedand when the event value isn’t null,you can create an authenticated client.<code_start>var httpClient = (await _googleSignIn.authenticatedClient())!;<code_end>This Client instance includes the necessarycredentials when invoking Google API classes.<topic_end><topic_start>5. Create and use the desired API classUse the API to create the desired API type and call methods.For instance:<code_start>var youTubeApi = YouTubeApi(httpClient);var favorites = await youTubeApi.playlistItems.list(  ['snippet'],  playlistId: 'LL', // Liked List);<code_end><topic_end><topic_start>More informationYou might want to check out the following:<topic_end><topic_start>Supported deployment platformsAs of Flutter 3.19.3,Flutter supports deploying apps the following combinations ofhardware architectures and operating system versions.These combinations are called platforms.Flutter supports platforms in three tiers:Based on these tiers, Flutter supports deploying to the following platforms.Android supports IA32 (x86) in software emulation only.<topic_end><topic_start>Desktop support for FlutterFlutter provides support for compilinga native Windows, macOS, or Linux desktop app.Flutter’s desktop support also extends to plugins—youcan install existing plugins that support the Windows,macOS, or Linux platforms, or you can create your own.info Note  This page covers developing apps for all desktop  platforms. Once you’ve read this, you can dive into  specific platform information at the following links:<topic_end><topic_start>Create a new projectYou can use the following stepsto create a new project with desktop support.<topic_end><topic_start>Set up desktop devtoolsConsult the guide for your target desktop environment:If flutter doctor finds problems or missing componentsfor a platform that you don’t want to develop for,you can ignore those warnings. Or you can disable theplatform altogether using the flutter config command,for example:Other available flags:After enabling desktop support,restart your IDE so that it can detect the new device.<topic_end><topic_start>Create and runCreating a new project with desktop support is no differentthan creating a new Flutter project for other platforms.Once you’ve configured your environment for desktopsupport, you can create and run a desktop applicationeither in the IDE or from the command line.<topic_end><topic_start>Using an IDEAfter you’ve configured your environment to supportdesktop, make sure you restart the IDE if it wasalready running.Create a new application in your IDE and it automaticallycreates iOS, Android, web, and desktop versions of your app.From the device pulldown, select windows (desktop),macOS (desktop), or linux (desktop)and run your application to see it launch on the desktop.<topic_end><topic_start>From the command lineTo create a new application that includes desktop support(in addition to mobile and web support), run the following commands,substituting my_app with the name of your project:To launch your application from the command line,enter one of the following commands from the topof the package:info Note  If you do not supply the -d flag, flutter run lists  the available targets to choose from.<topic_end><topic_start>Build a release appTo generate a release build,run one of the following commands:<topic_end><topic_start>Add desktop support to an existing Flutter appTo add desktop support to an existing Flutter project,run the following command in a terminal from theroot project directory:This adds the necessary desktop files and directoriesto your existing Flutter project.To add only specific desktop platforms,change the platforms list to include onlythe platform(s) you want to add.<topic_end><topic_start>Plugin supportFlutter on the desktop supports using and creating plugins.To use a plugin that supports desktop,follow the steps for plugins in using packages.Flutter automatically adds the necessary native codeto your project, as with any other platform.<topic_end><topic_start>Writing a pluginWhen you start building your own plugins,you’ll want to keep federation in mind.Federation is the ability to define severaldifferent packages, each targeted at adifferent set of platforms, brought togetherinto a single plugin for ease of use by developers.For example, the Windows implementation of theurl_launcher is really url_launcher_windows,but a Flutter developer can simply add theurl_launcher package to their pubspec.yamlas a dependency and the build process pulls inthe correct implementation based on the target platform.Federation is handy because different teams withdifferent expertise can build plugin implementationsfor different platforms.You can add a new platform implementation to anyendorsed federated plugin on pub.dev,so long as you coordinate this effort with theoriginal plugin author.For more information, including informationabout endorsed plugins, see the following resources:<topic_end><topic_start>Samples and codelabsYou can run the following samples as desktop apps,as well as download and inspect the source code tolearn more about Flutter desktop support.<topic_end><topic_start>Writing custom platform-specific codeThis guide describes how to write custom platform-specific code.Some platform-specific functionality is availablethrough existing packages;see using packages.info Note  The information in this page is valid for most platforms,  but platform-specific code for the web generally uses  JS interoperability or the dart:html library instead.Flutter uses a flexible system that allows you to callplatform-specific APIs in a language that works directlywith those APIs:Flutter’s builtin platform-specific API supportdoesn’t rely on code generation,but rather on a flexible message passing style.Alternatively, you can use the Pigeonpackage for sending structured typesafe messageswith code generation:The Flutter portion of the app sends messages to its host,the non-Dart portion of the app, over a platform channel.The host listens on the platform channel, and receives the message.It then calls into any number of platform-specific APIs—usingthe native programming language—and sends a response back to theclient, the Flutter portion of the app.info Note  This guide addresses using the platform channel mechanism  if you need to use the platform’s APIs in a non-Dart language.  But you can also write platform-specific Dart code  in your Flutter app by inspecting the  defaultTargetPlatform property.  Platform adaptations lists some  platform-specific adaptations that Flutter  automatically performs for you in the framework.<topic_end><topic_start>Architectural overview: platform channelsMessages are passed between the client (UI)and host (platform) using platformchannels as illustrated in this diagram:Messages and responses are passed asynchronously,to ensure the user interface remains responsive.info Note  Even though Flutter sends messages to and from Dart asynchronously,  whenever you invoke a channel method, you must invoke that method on the  platform’s main thread. See the section on threading  for more information.On the client side, MethodChannel enables sendingmessages that correspond to method calls. On the platform side,MethodChannel on Android (MethodChannelAndroid) andFlutterMethodChannel on iOS (MethodChanneliOS)enable receiving method calls and sending back aresult. These classes allow you to develop a platform pluginwith very little ‘boilerplate’ code.info Note  If desired, method calls can also be sent in the reverse direction,  with the platform acting as client to methods implemented in Dart.  For a concrete example, check out the quick_actions plugin.<topic_end><topic_start>Platform channel data types support and codecsThe standard platform channels use a standard message codec that supportsefficient binary serialization of simple JSON-like values, such as booleans,numbers, Strings, byte buffers, and Lists and Maps of these(see StandardMessageCodec for details).The serialization and deserialization of these values to and frommessages happens automatically when you send and receive values.The following table shows how Dart values are received on theplatform side and vice versa:<topic_end><topic_start>Example: Calling platform-specific code using platform channelsThe following code demonstrates how to calla platform-specific API to retrieve and displaythe current battery level.  It usesthe Android BatteryManager API,the iOS device.batteryLevel API,the Windows GetSystemPowerStatus API,and the Linux UPower API with a singleplatform message, getBatteryLevel().The example adds the platform-specific code insidethe main app itself.  If you want to reuse theplatform-specific code for multiple apps,the project creation step is slightly different(see developing packages),but the platform channel codeis still written in the same way.info Note  The full, runnable source-code for this example is  available in /examples/platform_channel/  for Android with Java, iOS with Objective-C,  Windows with C++, and Linux with C.  For iOS with Swift,  see /examples/platform_channel_swift/.<topic_end><topic_start>Step 1: Create a new app projectStart by creating a new app:By default, our template supports writing Android code using Kotlin,or iOS code using Swift. To use Java or Objective-C,use the -i and/or -a flags:<topic_end><topic_start>Step 2: Create the Flutter platform clientThe app’s State class holds the current app state.Extend that to hold the current battery state.First, construct the channel. Use a MethodChannel with a singleplatform method that returns the battery level.The client and host sides of a channel are connected througha channel name passed in the channel constructor.All channel names used in a single app mustbe unique; prefix the channel name with a unique ‘domainprefix’, for example: samples.flutter.dev/battery.<code_start>import 'dart:async';import 'package:flutter/material.dart';import 'package:flutter/services.dart';<code_end><code_start>class _MyHomePageState extends State<MyHomePage> {  static const platform = MethodChannel('samples.flutter.dev/battery');  // Get battery level.<code_end>Next, invoke a method on the method channel,specifying the concrete method to call usingthe String identifier getBatteryLevel.The call might fail—for example,if the platform doesn’t support theplatform API (such as when running in a simulator),so wrap the invokeMethod call in a try-catch statement.Use the returned result to update the user interface state in _batteryLevelinside setState.<code_start>// Get battery level.String _batteryLevel = 'Unknown battery level.';Future<void> _getBatteryLevel() async {  String batteryLevel;  try {    final result = await platform.invokeMethod<int>('getBatteryLevel');    batteryLevel = 'Battery level at $result % .';  } on PlatformException catch (e) {    batteryLevel = "Failed to get battery level: '${e.message}'.";  }  setState(() {    _batteryLevel = batteryLevel;  });}<code_end>Finally, replace the build method from the template tocontain a small user interface that displays the batterystate in a string, and a button for refreshing the value.<code_start>@overrideWidget build(BuildContext context) {  return Material(    child: Center(      child: Column(        mainAxisAlignment: MainAxisAlignment.spaceEvenly,        children: [          ElevatedButton(            onPressed: _getBatteryLevel,            child: const Text('Get Battery Level'),          ),          Text(_batteryLevel),        ],      ),    ),  );}<code_end><topic_end><topic_start>Step 3: Add an Android platform-specific implementationStart by opening the Android host portion of your Flutter appin Android Studio:Start Android StudioSelect the menu item File > Open…Navigate to the directory holding your Flutter app,and select the android folder inside it. Click OK.Open the file MainActivity.kt located in the kotlin folder in theProject view.Inside the configureFlutterEngine() method, create a MethodChannel and callsetMethodCallHandler(). Make sure to use the same channel name aswas used on the Flutter client side.<code_start>import androidx.annotation.NonNullimport io.flutter.embedding.android.FlutterActivityimport io.flutter.embedding.engine.FlutterEngineimport io.flutter.plugin.common.MethodChannelclass MainActivity: FlutterActivity() {  private val CHANNEL = "samples.flutter.dev/battery"  override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {    super.configureFlutterEngine(flutterEngine)    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {      call, result ->      // This method is invoked on the main thread.      // TODO    }  }}<code_end>Add the Android Kotlin code that uses the Android battery APIs toretrieve the battery level. This code is exactly the same as youwould write in a native Android app.First, add the needed imports at the top of the file:<code_start>import android.content.Contextimport android.content.ContextWrapperimport android.content.Intentimport android.content.IntentFilterimport android.os.BatteryManagerimport android.os.Build.VERSIONimport android.os.Build.VERSION_CODES<code_end>Next, add the following method in the MainActivity class,below the configureFlutterEngine() method:<code_start>private fun getBatteryLevel(): Int {  val batteryLevel: Int  if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {    val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager    batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)  } else {    val intent = ContextWrapper(applicationContext).registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))    batteryLevel = intent!!.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100 / intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)  }  return batteryLevel}<code_end>Finally, complete the setMethodCallHandler() method added earlier.You need to handle a single platform method, getBatteryLevel(),so test for that in the call argument.The implementation of this platform method calls theAndroid code written in the previous step, and returns a response for boththe success and error cases using the result argument.If an unknown method is called, report that instead.Remove the following code:<code_start>MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {  call, result ->  // This method is invoked on the main thread.  // TODO}<code_end>And replace with the following:<code_start>MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {  // This method is invoked on the main thread.  call, result ->  if (call.method == "getBatteryLevel") {    val batteryLevel = getBatteryLevel()    if (batteryLevel != -1) {      result.success(batteryLevel)    } else {      result.error("UNAVAILABLE", "Battery level not available.", null)    }  } else {    result.notImplemented()  }}<code_end>Start by opening the Android host portion of your Flutter appin Android Studio:Start Android StudioSelect the menu item File > Open…Navigate to the directory holding your Flutter app,and select the android folder inside it. Click OK.Open the MainActivity.java file located in the java folder in theProject view.Next, create a MethodChannel and set a MethodCallHandlerinside the configureFlutterEngine() method.Make sure to use the same channel name as was used on theFlutter client side.<code_start>import androidx.annotation.NonNull;import io.flutter.embedding.android.FlutterActivity;import io.flutter.embedding.engine.FlutterEngine;import io.flutter.plugin.common.MethodChannel;public class MainActivity extends FlutterActivity {  private static final String CHANNEL = "samples.flutter.dev/battery";  @Override  public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {  super.configureFlutterEngine(flutterEngine);    new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)        .setMethodCallHandler(          (call, result) -> {            // This method is invoked on the main thread.            // TODO          }        );  }}<code_end>Add the Android Java code that uses the Android battery APIs toretrieve the battery level. This code is exactly the same as youwould write in a native Android app.First, add the needed imports at the top of the file:<code_start>import android.content.ContextWrapper;import android.content.Intent;import android.content.IntentFilter;import android.os.BatteryManager;import android.os.Build.VERSION;import android.os.Build.VERSION_CODES;import android.os.Bundle;<code_end>Then add the following as a new method in the activity class,below the configureFlutterEngine() method:<code_start>private int getBatteryLevel() {  int batteryLevel = -1;  if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {    BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);    batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);  } else {    Intent intent = new ContextWrapper(getApplicationContext()).        registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));    batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /        intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);  }  return batteryLevel;}<code_end>Finally, complete the setMethodCallHandler() method added earlier.You need to handle a single platform method, getBatteryLevel(),so test for that in the call argument. The implementation ofthis platform method calls the Android code writtenin the previous step, and returns a response for boththe success and error cases using the result argument.If an unknown method is called, report that instead.Remove the following code:<code_start>(call, result) -> {  // This method is invoked on the main thread.  // TODO}<code_end>And replace with the following:<code_start>(call, result) -> {  // This method is invoked on the main thread.  if (call.method.equals("getBatteryLevel")) {    int batteryLevel = getBatteryLevel();    if (batteryLevel != -1) {      result.success(batteryLevel);    } else {      result.error("UNAVAILABLE", "Battery level not available.", null);    }  } else {    result.notImplemented();  }}<code_end>You should now be able to run the app on Android. If using the AndroidEmulator, set the battery level in the Extended Controls panelaccessible from the … button in the toolbar.<topic_end><topic_start>Step 4: Add an iOS platform-specific implementationStart by opening the iOS host portion of your Flutter app in Xcode:Start Xcode.Select the menu item File > Open….Navigate to the directory holding your Flutter app, and select the iosfolder inside it. Click OK.Add support for Swift in the standard template setup that uses Objective-C:Expand Runner > Runner in the Project navigator.Open the file AppDelegate.swift located under Runner > Runnerin the Project navigator.Override the application:didFinishLaunchingWithOptions: function and createa FlutterMethodChannel tied to the channel namesamples.flutter.dev/battery:<code_start>@UIApplicationMain@objc class AppDelegate: FlutterAppDelegate {  override func application(    _ application: UIApplication,    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {    let controller : FlutterViewController = window?.rootViewController as! FlutterViewController    let batteryChannel = FlutterMethodChannel(name: "samples.flutter.dev/battery",                                              binaryMessenger: controller.binaryMessenger)    batteryChannel.setMethodCallHandler({      (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in      // This method is invoked on the UI thread.      // Handle battery messages.    })    GeneratedPluginRegistrant.register(with: self)    return super.application(application, didFinishLaunchingWithOptions: launchOptions)  }}<code_end>Next, add the iOS Swift code that uses the iOS battery APIs to retrievethe battery level. This code is exactly the same as youwould write in a native iOS app.Add the following as a new method at the bottom of AppDelegate.swift:<code_start>private func receiveBatteryLevel(result: FlutterResult) {  let device = UIDevice.current  device.isBatteryMonitoringEnabled = true  if device.batteryState == UIDevice.BatteryState.unknown {    result(FlutterError(code: "UNAVAILABLE",                        message: "Battery level not available.",                        details: nil))  } else {    result(Int(device.batteryLevel * 100))  }}<code_end>Finally, complete the setMethodCallHandler() method added earlier.You need to handle a single platform method, getBatteryLevel(),so test for that in the call argument.The implementation of this platform method callsthe iOS code written in the previous step. If an unknown methodis called, report that instead.<code_start>batteryChannel.setMethodCallHandler({  [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in  // This method is invoked on the UI thread.  guard call.method == "getBatteryLevel" else {    result(FlutterMethodNotImplemented)    return  }  self?.receiveBatteryLevel(result: result)})<code_end>Start by opening the iOS host portion of the Flutter app in Xcode:Start Xcode.Select the menu item File > Open….Navigate to the directory holding your Flutter app,and select the ios folder inside it. Click OK.Make sure the Xcode projects builds without errors.Open the file AppDelegate.m, located under Runner > Runnerin the Project navigator.Create a FlutterMethodChannel and add a handler inside the applicationdidFinishLaunchingWithOptions: method.Make sure to use the same channel nameas was used on the Flutter client side.<code_start>#import <Flutter/Flutter.h>#import "GeneratedPluginRegistrant.h"@implementation AppDelegate- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {  FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;  FlutterMethodChannel* batteryChannel = [FlutterMethodChannel                                          methodChannelWithName:@"samples.flutter.dev/battery"                                          binaryMessenger:controller.binaryMessenger];  [batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {    // This method is invoked on the UI thread.    // TODO  }];  [GeneratedPluginRegistrant registerWithRegistry:self];  return [super application:application didFinishLaunchingWithOptions:launchOptions];}<code_end>Next, add the iOS ObjectiveC code that uses the iOS battery APIs toretrieve the battery level. This code is exactly the same as youwould write in a native iOS app.Add the following method in the AppDelegate class, just before @end:<code_start>- (int)getBatteryLevel {  UIDevice* device = UIDevice.currentDevice;  device.batteryMonitoringEnabled = YES;  if (device.batteryState == UIDeviceBatteryStateUnknown) {    return -1;  } else {    return (int)(device.batteryLevel * 100);  }}<code_end>Finally, complete the setMethodCallHandler() method added earlier.You need to handle a single platform method, getBatteryLevel(),so test for that in the call argument. The implementation ofthis platform method calls the iOS code written in the previous step,and returns a response for both the success and error cases usingthe result argument. If an unknown method is called, report that instead.<code_start>__weak typeof(self) weakSelf = self;[batteryChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {  // This method is invoked on the UI thread.  if ([@"getBatteryLevel" isEqualToString:call.method]) {    int batteryLevel = [weakSelf getBatteryLevel];    if (batteryLevel == -1) {      result([FlutterError errorWithCode:@"UNAVAILABLE"                                 message:@"Battery level not available."                                 details:nil]);    } else {      result(@(batteryLevel));    }  } else {    result(FlutterMethodNotImplemented);  }}];<code_end>You should now be able to run the app on iOS.If using the iOS Simulator,note that it doesn’t support battery APIs,and the app displays ‘Battery level not available’.<topic_end><topic_start>Step 5: Add a Windows platform-specific implementationStart by opening the Windows host portion of your Flutter app in Visual Studio:Run flutter build windows in your project directory once to generatethe Visual Studio solution file.Start Visual Studio.Select Open a project or solution.Navigate to the directory holding your Flutter app, then into the buildfolder, then the windows folder, then select the batterylevel.sln file.Click Open.Add the C++ implementation of the platform channel method:Expand batterylevel > Source Files in the Solution Explorer.Open the file flutter_window.cpp.First, add the necessary includes to the top of the file, justafter #include "flutter_window.h":<code_start>#include <flutter/event_channel.h>#include <flutter/event_sink.h>#include <flutter/event_stream_handler_functions.h>#include <flutter/method_channel.h>#include <flutter/standard_method_codec.h>#include <windows.h>#include <memory><code_end>Edit the FlutterWindow::OnCreate method and createa flutter::MethodChannel tied to the channel namesamples.flutter.dev/battery:<code_start>bool FlutterWindow::OnCreate() {  // ...  RegisterPlugins(flutter_controller_->engine());  flutter::MethodChannel<> channel(      flutter_controller_->engine()->messenger(), "samples.flutter.dev/battery",      &flutter::StandardMethodCodec::GetInstance());  channel.SetMethodCallHandler(      [](const flutter::MethodCall<>& call,         std::unique_ptr<flutter::MethodResult<>> result) {        // TODO      });  SetChildContent(flutter_controller_->view()->GetNativeWindow());  return true;}<code_end>Next, add the C++ code that uses the Windows battery APIs toretrieve the battery level. This code is exactly the same asyou would write in a native Windows application.Add the following as a new function at the top offlutter_window.cpp just after the #include section:<code_start>static int GetBatteryLevel() {  SYSTEM_POWER_STATUS status;  if (GetSystemPowerStatus(&status) == 0 || status.BatteryLifePercent == 255) {    return -1;  }  return status.BatteryLifePercent;}<code_end>Finally, complete the setMethodCallHandler() method added earlier.You need to handle a single platform method, getBatteryLevel(),so test for that in the call argument.The implementation of this platform method callsthe Windows code written in the previous step. If an unknown methodis called, report that instead.Remove the following code:<code_start>channel.SetMethodCallHandler(    [](const flutter::MethodCall<>& call,       std::unique_ptr<flutter::MethodResult<>> result) {      // TODO    });<code_end>And replace with the following:<code_start>channel.SetMethodCallHandler(    [](const flutter::MethodCall<>& call,       std::unique_ptr<flutter::MethodResult<>> result) {      if (call.method_name() == "getBatteryLevel") {        int battery_level = GetBatteryLevel();        if (battery_level != -1) {          result->Success(battery_level);        } else {          result->Error("UNAVAILABLE", "Battery level not available.");        }      } else {        result->NotImplemented();      }    });<code_end>You should now be able to run the application on Windows.If your device doesn’t have a battery,it displays ‘Battery level not available’.<topic_end><topic_start>Step 6: Add a macOS platform-specific implementationStart by opening the macOS host portion of your Flutter app in Xcode:Start Xcode.Select the menu item File > Open….Navigate to the directory holding your Flutter app, and select the macosfolder inside it. Click OK.Add the Swift implementation of the platform channel method:Expand Runner > Runner in the Project navigator.Open the file MainFlutterWindow.swift located under Runner > Runnerin the Project navigator.First, add the necessary import to the top of the file, just afterimport FlutterMacOS:<code_start>import IOKit.ps<code_end>Create a FlutterMethodChannel tied to the channel namesamples.flutter.dev/battery in the awakeFromNib method:<code_start>  override func awakeFromNib() {    // ...    self.setFrame(windowFrame, display: true)    let batteryChannel = FlutterMethodChannel(      name: "samples.flutter.dev/battery",      binaryMessenger: flutterViewController.engine.binaryMessenger)    batteryChannel.setMethodCallHandler { (call, result) in      // This method is invoked on the UI thread.      // Handle battery messages.    }    RegisterGeneratedPlugins(registry: flutterViewController)    super.awakeFromNib()  }}<code_end>Next, add the macOS Swift code that uses the IOKit battery APIs to retrievethe battery level. This code is exactly the same as youwould write in a native macOS app.Add the following as a new method at the bottom of MainFlutterWindow.swift:<code_start>private func getBatteryLevel() -> Int? {  let info = IOPSCopyPowerSourcesInfo().takeRetainedValue()  let sources: Array<CFTypeRef> = IOPSCopyPowerSourcesList(info).takeRetainedValue() as Array  if let source = sources.first {    let description =      IOPSGetPowerSourceDescription(info, source).takeUnretainedValue() as! [String: AnyObject]    if let level = description[kIOPSCurrentCapacityKey] as? Int {      return level    }  }  return nil}<code_end>Finally, complete the setMethodCallHandler method added earlier.You need to handle a single platform method, getBatteryLevel(),so test for that in the call argument.The implementation of this platform method callsthe macOS code written in the previous step. If an unknown methodis called, report that instead.<code_start>batteryChannel.setMethodCallHandler { (call, result) in  switch call.method {  case "getBatteryLevel":    guard let level = getBatteryLevel() else {      result(        FlutterError(          code: "UNAVAILABLE",          message: "Battery level not available",          details: nil))     return    }    result(level)  default:    result(FlutterMethodNotImplemented)  }}<code_end>You should now be able to run the application on macOS.If your device doesn’t have a battery,it displays ‘Battery level not available’.<topic_end><topic_start>Step 7: Add a Linux platform-specific implementationFor this example you need to install the upower developer headers.This is likely available from your distribution, for example with:Start by opening the Linux host portion of your Flutter app in the editorof your choice. The instructions below are for Visual Studio Code with the“C/C++” and “CMake” extensions installed, but can be adjusted for other IDEs.Launch Visual Studio Code.Open the linux directory inside your project.Choose Yes in the prompt asking: Would you like to configure project "linux"?.This enables C++ autocomplete.Open the file my_application.cc.First, add the necessary includes to the top of the file, justafter #include <flutter_linux/flutter_linux.h:<code_start>#include <math.h>#include <upower.h><code_end>Add an FlMethodChannel to the _MyApplication struct:<code_start>struct _MyApplication {  GtkApplication parent_instance;  char** dart_entrypoint_arguments;  FlMethodChannel* battery_channel;};<code_end>Make sure to clean it up in my_application_dispose:<code_start>static void my_application_dispose(GObject* object) {  MyApplication* self = MY_APPLICATION(object);  g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);  g_clear_object(&self->battery_channel);  G_OBJECT_CLASS(my_application_parent_class)->dispose(object);}<code_end>Edit the my_application_activate method and initializebattery_channel using the channel namesamples.flutter.dev/battery, just after the call tofl_register_plugins:<code_start>static void my_application_activate(GApplication* application) {  // ...  fl_register_plugins(FL_PLUGIN_REGISTRY(self->view));  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();  self->battery_channel = fl_method_channel_new(      fl_engine_get_binary_messenger(fl_view_get_engine(view)),      "samples.flutter.dev/battery", FL_METHOD_CODEC(codec));  fl_method_channel_set_method_call_handler(      self->battery_channel, battery_method_call_handler, self, nullptr);  gtk_widget_grab_focus(GTK_WIDGET(self->view));}<code_end>Next, add the C code that uses the Linux battery APIs toretrieve the battery level. This code is exactly the same asyou would write in a native Linux application.Add the following as a new function at the top ofmy_application.cc just after the G_DEFINE_TYPE line:<code_start>static FlMethodResponse* get_battery_level() {  // Find the first available battery and report that.  g_autoptr(UpClient) up_client = up_client_new();  g_autoptr(GPtrArray) devices = up_client_get_devices2(up_client);  if (devices->len == 0) {    return FL_METHOD_RESPONSE(fl_method_error_response_new(        "UNAVAILABLE", "Device does not have a battery.", nullptr));  }  UpDevice* device = (UpDevice*)(g_ptr_array_index(devices, 0));  double percentage = 0;  g_object_get(device, "percentage", &percentage, nullptr);  g_autoptr(FlValue) result =      fl_value_new_int(static_cast<int64_t>(round(percentage)));  return FL_METHOD_RESPONSE(fl_method_success_response_new(result));}<code_end>Finally, add the battery_method_call_handler function referencedin the earlier call to fl_method_channel_set_method_call_handler.You need to handle a single platform method, getBatteryLevel,so test for that in the method_call argument.The implementation of this function callsthe Linux code written in the previous step. If an unknown methodis called, report that instead.Add the following code after the get_battery_level function:<code_start>static void battery_method_call_handler(FlMethodChannel* channel,                                        FlMethodCall* method_call,                                        gpointer user_data) {  g_autoptr(FlMethodResponse) response = nullptr;  if (strcmp(fl_method_call_get_name(method_call), "getBatteryLevel") == 0) {    response = get_battery_level();  } else {    response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());  }  g_autoptr(GError) error = nullptr;  if (!fl_method_call_respond(method_call, response, &error)) {    g_warning("Failed to send response: %s", error->message);  }}<code_end>You should now be able to run the application on Linux.If your device doesn’t have a battery,it displays ‘Battery level not available’.<topic_end><topic_start>Typesafe platform channels using PigeonThe previous example uses MethodChannelto communicate between the host and client,which isn’t typesafe. Calling and receivingmessages depends on the host and client declaringthe same arguments and datatypes in order for messages to work.You can use the Pigeon package asan alternative to MethodChannelto generate code that sends messages in astructured, typesafe manner.With Pigeon, the messaging protocol is definedin a subset of Dart that then generates messagingcode for Android, iOS, macOS, or Windows. You can find a more completeexample and more information on the pigeonpage on pub.dev.Using Pigeon eliminates the need to matchstrings between host and clientfor the names and datatypes of messages.It supports: nested classes, groupingmessages into APIs, generation ofasynchronous wrapper code and sending messagesin either direction. The generated code is readableand guarantees there are no conflicts betweenmultiple clients of different versions.Supported languages are Objective-C, Java, Kotlin, C++,and Swift (with Objective-C interop).<topic_end><topic_start>Pigeon examplePigeon file:<code_start>import 'package:pigeon/pigeon.dart';class SearchRequest {  final String query;  SearchRequest({required this.query});}class SearchReply {  final String result;  SearchReply({required this.result});}@HostApi()abstract class Api {  @async  SearchReply search(SearchRequest request);}<code_end>Dart usage:<code_start>import 'generated_pigeon.dart';Future<void> onClick() async {  SearchRequest request = SearchRequest(query: 'test');  Api api = SomeApi();  SearchReply reply = await api.search(request);  print('reply: ${reply.result}');}<code_end><topic_end><topic_start>Separate platform-specific code from UI codeIf you expect to use your platform-specific codein multiple Flutter apps, you might considerseparating the code into a platform plugin locatedin a directory outside your main application.See developing packages for details.<topic_end><topic_start>Publish platform-specific code as a packageTo share your platform-specific code with other developersin the Flutter ecosystem, see publishing packages.<topic_end><topic_start>Custom channels and codecsBesides the above mentioned MethodChannel,you can also use the more basicBasicMessageChannel, which supports basic,asynchronous message passing using a custom message codec.You can also use the specialized BinaryCodec,StringCodec, and JSONMessageCodecclasses, or create your own codec.You might also check out an example of a custom codecin the cloud_firestore plugin,which is able to serialize and deserialize many moretypes than the default types.<topic_end><topic_start>Channels and platform threadingWhen invoking channels on the platform side destined for Flutter,invoke them on the platform’s main thread.When invoking channels in Flutter destined for the platform side,either invoke them from any Isolate that is the rootIsolate, or that is registered as a background Isolate.The handlers for the platform side can execute on the platform’s main threador they can execute on a background thread if using a Task Queue.You can invoke the platform side handlers asynchronouslyand on any thread.info Note  On Android, the platform’s main thread is sometimes  called the “main thread”, but it is technically defined  as the UI thread. Annotate methods that need  to be run on the UI thread with @UiThread.  On iOS, this thread is officially  referred to as the main thread.<topic_end><topic_start>Using plugins and channels from background isolatesPlugins and channels can be used by any Isolate, but that Isolate has to bea root Isolate (the one created by Flutter) or registered as a backgroundIsolate for a root Isolate.The following example shows how to register a background Isolate in order touse a plugin from a background Isolate.<topic_end><topic_start>Executing channel handlers on background threadsIn order for a channel’s platform side handler toexecute on a background thread, you must use theTask Queue API. Currently this feature is onlysupported on iOS and Android.In Java:In Kotlin:In Swift:info Note  In release 2.10, the Task Queue API is only available on the master channel  for iOS.In Objective-C:info Note  In release 2.10, the Task Queue API is only available on the master channel  for iOS.<topic_end><topic_start>Jumping to the UI thread in AndroidTo comply with channels’ UI thread requirement,you might need to jump from a background threadto Android’s UI thread to execute a channel method.In Android, you can accomplish this by post()ing aRunnable to Android’s UI thread Looper,which causes the Runnable to execute on themain thread at the next opportunity.In Java:In Kotlin:<topic_end><topic_start>Jumping to the main thread in iOSTo comply with channel’s main thread requirement,you might need to jump from a background thread toiOS’s main thread to execute a channel method.You can accomplish this in iOS by executing ablock on the main dispatch queue:In Objective-C:In Swift:<topic_end><topic_start>Automatic platform adaptationsinfo Note  As of the Flutter 3.16 release, Material 3  replaces Material 2 as the default theme  on all Flutter apps that use Material.<topic_end><topic_start>Adaptation philosophyIn general, two cases of platform adaptiveness exist:This article mainly covers the automatic adaptationsprovided by Flutter in case 1 on Android and iOS.For case 2, Flutter bundles the means to produce theappropriate effects of the platform conventions but doesn’tadapt automatically when app design choices are needed.For a discussion, see issue #8410 and theMaterial/Cupertino adaptive widget problem definition.For an example of an app using different informationarchitecture structures on Android and iOS but sharingthe same content code, see the platform_design code samples.infoPreliminary guides addressing case 2 are being added to the UI components section. You can request additional guides by commenting on issue #8427.<topic_end><topic_start>Page navigationFlutter provides the navigation patterns seen on Androidand iOS and also automatically adapts the navigation animationto the current platform.<topic_end><topic_start>Navigation transitionsOn Android, the default Navigator.push() transitionis modeled after startActivity(),which generally has one bottom-up animation variant.On iOS:<topic_end><topic_start>Platform-specific transition detailsOn Android, Flutter uses the ZoomPageTransitionsBuilder animation.When the user taps on an item, the UI zooms in to a screen that features that item.When the user taps to go back, the UI zooms out to the previous screen.On iOS when the push style transition is used,Flutter’s bundled CupertinoNavigationBarand CupertinoSliverNavigationBar nav barsautomatically animate each subcomponent to its correspondingsubcomponent on the next or previous page’sCupertinoNavigationBar or CupertinoSliverNavigationBar.<topic_end><topic_start>Back navigationOn Android,the OS back button, by default, is sent to Flutterand pops the top route of the WidgetsApp’s Navigator.On iOS,an edge swipe gesture can be used to pop the top route.<topic_end><topic_start>ScrollingScrolling is an important part of the platform’slook and feel, and Flutter automatically adjuststhe scrolling behavior to match the current platform.<topic_end><topic_start>Physics simulationAndroid and iOS both have complex scrolling physicssimulations that are difficult to describe verbally.Generally, iOS’s scrollable has more weight anddynamic friction but Android has more static friction.Therefore iOS gains high speed more gradually but stopsless abruptly and is more slippery at slow speeds.<topic_end><topic_start>Overscroll behaviorOn Android,scrolling past the edge of a scrollable shows anoverscroll glow indicator (based on the colorof the current Material theme).On iOS, scrolling past the edge of a scrollableoverscrolls with increasing resistance and snaps back.<topic_end><topic_start>MomentumOn iOS,repeated flings in the same direction stacks momentumand builds more speed with each successive fling.There is no equivalent behavior on Android.<topic_end><topic_start>Return to topOn iOS,tapping the OS status bar scrolls the primaryscroll controller to the top position.There is no equivalent behavior on Android.<topic_end><topic_start>TypographyWhen using the Material package,the typography automatically defaults to thefont family appropriate for the platform.Android uses the Roboto font.iOS uses the San Francisco font.When using the Cupertino package, the default themeuses the San Francisco font.The San Francisco font license limits its usage tosoftware running on iOS, macOS, or tvOS only.Therefore a fallback font is used when running on Androidif the platform is debug-overridden to iOS or thedefault Cupertino theme is used.You might choose to adapt the text styling of Material widgets to match the default text styling on iOS. You can see widget-specific examples in the UI Component section.<topic_end><topic_start>IconographyWhen using the Material package,certain icons automatically show differentgraphics depending on the platform.For instance, the overflow button’s three dotsare horizontal on iOS and vertical on Android.The back button is a simple chevron on iOS andhas a stem/shaft on Android.The material library also provides a set of platform-adaptive icons through Icons.adaptive.<topic_end><topic_start>Haptic feedbackThe Material and Cupertino packages automaticallytrigger the platform appropriate haptic feedback incertain scenarios.For instance,a word selection via text field long-press triggers a ‘buzz’vibrate on Android and not on iOS.Scrolling through picker items on iOS triggers a‘light impact’ knock and no feedback on Android.<topic_end><topic_start>Text editingFlutter also makes the below adaptations while editingthe content of text fields to match the current platform.<topic_end><topic_start>Keyboard gesture navigationOn Android,horizontal swipes can be made on the soft keyboard’s space keyto move the cursor in Material and Cupertino text fields.On iOS devices with 3D Touch capabilities,a force-press-drag gesture could be made on the softkeyboard to move the cursor in 2D via a floating cursor.This works on both Material and Cupertino text fields.<topic_end><topic_start>Text selection toolbarWith Material on Android,the Android style selection toolbar is shown whena text selection is made in a text field.With Material on iOS or when using Cupertino,the iOS style selection toolbar is shown when a textselection is made in a text field.<topic_end><topic_start>Single tap gestureWith Material on Android,a single tap in a text field puts the cursor at thelocation of the tap.A collapsed text selection also shows a draggablehandle to subsequently move the cursor.With Material on iOS or when using Cupertino,a single tap in a text field puts the cursor at thenearest edge of the word tapped.Collapsed text selections don’t have draggable handles on iOS.<topic_end><topic_start>Long-press gestureWith Material on Android,a long press selects the word under the long press.The selection toolbar is shown upon release.With Material on iOS or when using Cupertino,a long press places the cursor at the location of thelong press. The selection toolbar is shown upon release.<topic_end><topic_start>Long-press drag gestureWith Material on Android,dragging while holding the long press expands the words selected.With Material on iOS or when using Cupertino,dragging while holding the long press moves the cursor.<topic_end><topic_start>Double tap gestureOn both Android and iOS,a double tap selects the word receiving thedouble tap and shows the selection toolbar.<topic_end><topic_start>UI componentsThis section includes preliminary recommendations on how to adapt Material widgets to deliver a natural and compelling experience on iOS. Your feedback is welcomed on issue #8427.<topic_end><topic_start>Widgets with .adaptive() constructorsSeveral widgets support .adaptive() constructors. The following table lists these widgets.Adaptive constructors substitute the corresponding Cupertino components when the app is run on an iOS device.Widgets in the following table are used primarily for input, selection, and to display system information. Because these controls are tightly integrated with the operating system,users have been trained to recognize and respond to them.Therefore, we recommend that you follow platform conventions.<topic_end><topic_start>Top app bar and navigation barSince Android 12, the default UI for top app bars follows the design guidelines defined in Material 3. On iOS, an equivalent component called “Navigation Bars” is defined in Apple’s Human Interface Guidelines (HIG).Certain properties of app bars in Flutter apps should be adapted, like system icons and page transitions. These are already automatically adapted when using the Material AppBar and SliverAppBar widgets. You can also further customize the properties of these widgets to better match iOS platform styles, as shown below.But, because app bars are displayed alongside other content in your page, it’s only recommended to adapt the styling so long as its cohesive with the rest of your application. You can see additional code samples and a further explanation in the GitHub discussion on app bar adaptations.<topic_end><topic_start>Bottom navigation barsSince Android 12, the default UI for bottom navigation bars follow the design guidelines defined in Material 3. On iOS, an equivalent component called “Tab Bars” is defined in Apple’s Human Interface Guidelines (HIG).Since tab bars are persistent across your app, they should match yourown branding. However, if you choose to use Material’s default styling on Android, you might consider adapting to the default iOStab bars.To implement platform-specific bottom navigation bars, you can use Flutter’s NavigationBar widget on Android and the CupertinoTabBar widget on iOS. Below is a code snippet you can adapt to show a platform-specific navigation bars.<topic_end><topic_start>Text fieldsSince Android 12, text fields follow theMaterial 3 (M3) design guidelines. On iOS, Apple’s Human Interface Guidelines (HIG) definean equivalent component.Since text fields require user input,their design should follow platform conventions.To implement a platform-specific TextField in Flutter, you can adapt the styling of the Material TextField.To learn more about adapting text fields, check out the GitHub discussion on text fields.You can leave feedback or ask questions in the discussion.<topic_end><topic_start>Alert dialogSince Android 12, the default UI of alert dialogs (also known as a “basic dialog”) follows the design guidelines defined in Material 3 (M3). On iOS, an equivalent component called “alert” is defined in Apple’s Human Interface Guidelines (HIG).Since alert dialogs are often tightly integrated with the operating system, their design generally needs to follow the platform conventions. This is especially important when a dialog is used to request user input about security, privacy, and destructive operations (e.g., deleting files permanently). As an exception, a branded alert dialog design can be used on non-critical user flows to highlight specific information or messages.To implement platform-specific alert dialogs, you can use Flutter’s AlertDialog widget on Android and the CupertinoAlertDialog widget on iOS. Below is a code snippet you can adapt to show a platform-specific alert dialog.To learn more about adapting alert dialogs, check out the GitHub discussion on dialog adaptations.You can leave feedback or ask questions in the discussion.<topic_end><topic_start>Android<topic_end><topic_start>Topics<topic_end><topic_start>Add Android devtools for FlutterTo choose the guide to add Android Studio to your Flutter configuration,click the Getting Started path you followed.<topic_end><topic_start>Adding a splash screen to your Android appSplash screens (also known as launch screens) provide a simple initial experience while your Android app loads. They set the stage for your application, while allowing time for the app engine to load and your app to initialize.<topic_end><topic_start>Overviewwarning Warning  If you are experiencing a crash from implementing a splash screen, you  might need to migrate your code. See detailed instructions in the  Deprecated Splash Screen API Migration guide.In Android, there are two separate screens that you can control:a launch screen shown while your Android app initializes,and a splash screen that displays while the Flutter experienceinitializes.info Note  As of Flutter 2.5, the launch and splash screens have been  consolidated—Flutter now only implements the Android launch screen,  which is displayed until the framework draws the first frame.  This launch screen can act as both an Android launch screen and an  Android splash screen via customization, and thus, is referred to  as both terms. For example of such customization, check out the  Android splash screen sample app.If, prior to 2.5, you used flutter create to create an app,  and you run the app on 2.5 or later, the app might crash.  For more info, see the Deprecated Splash Screen API Migration guide.info Note  For apps that embed one or more Flutter screens within an  existing Android app, consider  pre-warming a FlutterEngine and reusing the  same engine throughout your app to minimize wait  time associated with initialization of the Flutter engine.<topic_end><topic_start>Initializing the appEvery Android app requires initialization time while theoperating system sets up the app’s process.Android provides the concept of a launch screen todisplay a Drawable while the app is initializing.A Drawable is an Android graphic.To learn how to add a Drawable to yourFlutter project in Android Studio,check out Import drawables into your projectin the Android developer documentation.The default Flutter project template includes a definitionof a launch theme and a launch background. You can customizethis by editing styles.xml, where you can define a themewhose windowBackground is set to theDrawable that should be displayed as the launch screen.In addition, styles.xml defines a normal themeto be applied to FlutterActivity after the launchscreen is gone. The normal theme background only showsfor a very brief moment after the splash screen disappears,and during orientation change and Activity restoration.Therefore, it’s recommended that the normal theme use asolid background color that looks similar to the primarybackground color of the Flutter UI.<topic_end><topic_start>Set up the FlutterActivity in AndroidManifest.xmlIn AndroidManifest.xml, set the theme ofFlutterActivity to the launch theme. Then,add a metadata element to the desired FlutterActivityto instruct Flutter to switch from the launch themeto the normal theme at the appropriate time.The Android app now displays the desired launch screenwhile the app initializes.<topic_end><topic_start>Android 12To configure your launch screen on Android 12,check out Android Splash Screens.As of Android 12, you must use the new splash screenAPI in your styles.xml file.Consider creating an alternate resource file for Android 12 and higher.Also make sure that your background image is in line withthe icon guidelines;check out Android Splash Screens for more details.Make sure thatio.flutter.embedding.android.SplashScreenDrawable isnot set in your manifest, and that provideSplashScreenis not implemented, as these APIs are deprecated.Doing so causes the Android launch screen to fade smoothlyinto the Flutter when theapp is launched and the app might crash.Some apps might want to continue showing the last frame ofthe Android launch screen in Flutter. For example,this preserves the illusion of a single framewhile additional loading continues in Dart.To achieve this, the followingAndroid APIs might be helpful:<code_start>import android.os.Build;import android.os.Bundle;import android.window.SplashScreenView;import androidx.core.view.WindowCompat;import io.flutter.embedding.android.FlutterActivity;public class MainActivity extends FlutterActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        // Aligns the Flutter view vertically with the window.        WindowCompat.setDecorFitsSystemWindows(getWindow(), false);        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {            // Disable the Android splash screen fade out animation to avoid            // a flicker before the similar frame is drawn in Flutter.            getSplashScreen()                .setOnExitAnimationListener(                    (SplashScreenView splashScreenView) -> {                        splashScreenView.remove();                    });        }        super.onCreate(savedInstanceState);    }}<code_end><code_start>import android.os.Buildimport android.os.Bundleimport androidx.core.view.WindowCompatimport io.flutter.embedding.android.FlutterActivityclass MainActivity : FlutterActivity() {  override fun onCreate(savedInstanceState: Bundle?) {    // Aligns the Flutter view vertically with the window.    WindowCompat.setDecorFitsSystemWindows(getWindow(), false)    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {      // Disable the Android splash screen fade out animation to avoid      // a flicker before the similar frame is drawn in Flutter.      splashScreen.setOnExitAnimationListener { splashScreenView -> splashScreenView.remove() }    }    super.onCreate(savedInstanceState)  }}<code_end>Then, you can reimplement the first frame in Flutterthat shows elements of your Android launch screen inthe same positions on screen.For an example of this, check out theAndroid splash screen sample app.<topic_end><topic_start>Binding to native Android code using dart:ffiFlutter mobile and desktop apps can use thedart:ffi library to call native C APIs.FFI stands for foreign function interface.Other terms for similar functionality includenative interface and language bindings.info Note  This page describes using the dart:ffi library  in Android apps. For information on iOS, see  Binding to native iOS code using dart:ffi.  For information in macOS, see  Binding to native macOS code using dart:ffi.  This feature is not yet supported for web plugins.Before your library or program can use the FFI libraryto bind to native code, you must ensure that thenative code is loaded and its symbols are visible to Dart.This page focuses on compiling, packaging,and loading Android native code within a Flutter plugin or app.This tutorial demonstrates how to bundle C/C++sources in a Flutter plugin and bind to them usingthe Dart FFI library on both Android and iOS.In this walkthrough, you’ll create a C functionthat implements 32-bit addition and thenexposes it through a Dart plugin named “native_add”.<topic_end><topic_start>Dynamic vs static linkingA native library can be linked into an app eitherdynamically or statically. A statically linked libraryis embedded into the app’s executable image,and is loaded when the app starts.Symbols from a statically linked library can beloaded using DynamicLibrary.executable orDynamicLibrary.process.A dynamically linked library, by contrast, is distributedin a separate file or folder within the app,and loaded on-demand. On Android, a dynamicallylinked library is distributed as a set of .so (ELF)files, one for each architecture.A dynamically linked library can be loaded intoDart via DynamicLibrary.open.API documentation is available from the Dart dev channel:Dart API reference documentation.On Android, only dynamic libraries are supported(because the main executable is the JVM,which we don’t link to statically).<topic_end><topic_start>Create an FFI pluginTo create an FFI plugin called “native_add”,do the following:info Note  You can exclude platforms from --platforms that you don’t want  to build to. However, you need to include the platform of   the device you are testing on.This will create a plugin with C/C++ sources in native_add/src.These sources are built by the native build files in the variousos build folders.The FFI library can only bind against C symbols,so in C++ these symbols are marked extern "C".You should also add attributes to indicate that thesymbols are referenced from Dart,to prevent the linker from discarding the symbolsduring link-time optimization.__attribute__((visibility("default"))) __attribute__((used)).On Android, the native_add/android/build.gradle links the code.The native code is invoked from dart in lib/native_add_bindings_generated.dart.The bindings are generated with package:ffigen.<topic_end><topic_start>Other use cases<topic_end><topic_start>Platform libraryTo link against a platform library,use the following instructions:Load the library using DynamicLibrary.open.For example, to load OpenGL ES (v3):You might need to update the Android manifestfile of the app or plugin if indicated bythe documentation.<topic_end><topic_start>First-party libraryThe process for including native code in sourcecode or binary form is the same for an app orplugin.<topic_end><topic_start>Open-source third-partyFollow the Add C and C++ code to your projectinstructions in the Android docs toadd native code and support for the nativecode toolchain (either CMake or ndk-build).<topic_end><topic_start>Closed-source third-party libraryTo create a Flutter plugin that includes Dartsource code, but distribute the C/C++ libraryin binary form, use the following instructions:<topic_end><topic_start>Android APK size (shared object compression)Android guidelines in general recommenddistributing native shared objects uncompressedbecause that actually saves on device space.Shared objects can be directly loaded from the APKinstead of unpacking them on device into atemporary location and then loading.APKs are additionally packed in transit—that’swhy you should be looking at download size.Flutter APKs by default don’t follow these guidelinesand compress libflutter.so and libapp.so—thisleads to smaller APK size but larger on device size.Shared objects from third parties can change this defaultsetting with android:extractNativeLibs="true" in theirAndroidManifest.xml and stop the compression of libflutter.so,libapp.so, and any user-added shared objects.To re-enable compression, override the setting inyour_app_name/android/app/src/main/AndroidManifest.xmlin the following way.<topic_end><topic_start>Other ResourcesTo learn more about C interoperability, check out these videos:<topic_end><topic_start>Hosting native Android views in your Flutter app with Platform ViewsPlatform views allow you to embed native views in a Flutter app,so you can apply transforms, clips, and opacity to the native viewfrom Dart.This allows you, for example, to use the nativeGoogle Maps from the Android SDKdirectly inside your Flutter app.info Note  This page discusses how to host your own native Android views  within a Flutter app.  If you’d like to embed native iOS views in your Flutter app,  see Hosting native iOS views.Flutter supports two modes:Hybrid composition and virtual displays.Which one to use depends on the use case.Let’s take a look:Hybrid compositionappends the native android.view.View to the view hierarchy. Therefore, keyboard handling, and accessibility work out of the box.Prior to Android 10, this mode might significantlyreduce the frame throughput (FPS) of the Flutter UI.For more context, see Performance.Virtual displaysrender the android.view.View instance to a texture, so it’s not embedded within the Android Activity’s view hierarchy.Certain platform interactions such as keyboard handlingand accessibility features might not work.To create a platform view on Android,use the following steps:<topic_end><topic_start>On the Dart sideOn the Dart side, create a Widgetand add one of the following build implementations.<topic_end><topic_start>Hybrid compositionIn your Dart file,for example native_view_example.dart,use the following instructions:Add the following imports:<code_start>import 'package:flutter/foundation.dart';import 'package:flutter/gestures.dart';import 'package:flutter/material.dart';import 'package:flutter/rendering.dart';import 'package:flutter/services.dart';<code_end>Implement a build() method:<code_start>Widget build(BuildContext context) {  // This is used in the platform side to register the view.  const String viewType = '<platform-view-type>';  // Pass parameters to the platform side.  const Map<String, dynamic> creationParams = <String, dynamic>{};  return PlatformViewLink(    viewType: viewType,    surfaceFactory: (context, controller) {      return AndroidViewSurface(        controller: controller as AndroidViewController,        gestureRecognizers: const <Factory<OneSequenceGestureRecognizer>>{},        hitTestBehavior: PlatformViewHitTestBehavior.opaque,      );    },    onCreatePlatformView: (params) {      return PlatformViewsService.initSurfaceAndroidView(        id: params.id,        viewType: viewType,        layoutDirection: TextDirection.ltr,        creationParams: creationParams,        creationParamsCodec: const StandardMessageCodec(),        onFocus: () {          params.onFocusChanged(true);        },      )        ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)        ..create();    },  );}<code_end>For more information, see the API docs for:<topic_end><topic_start>Virtual displayIn your Dart file,for example native_view_example.dart,use the following instructions:Add the following imports:<code_start>import 'package:flutter/material.dart';import 'package:flutter/services.dart';<code_end>Implement a build() method:<code_start>Widget build(BuildContext context) {  // This is used in the platform side to register the view.  const String viewType = '<platform-view-type>';  // Pass parameters to the platform side.  final Map<String, dynamic> creationParams = <String, dynamic>{};  return AndroidView(    viewType: viewType,    layoutDirection: TextDirection.ltr,    creationParams: creationParams,    creationParamsCodec: const StandardMessageCodec(),  );}<code_end>For more information, see the API docs for:<topic_end><topic_start>On the platform sideOn the platform side, use the standardio.flutter.plugin.platform packagein either Java or Kotlin:In your native code, implement the following:Extend io.flutter.plugin.platform.PlatformViewto provide a reference to the android.view.View(for example, NativeView.kt):Create a factory class that creates an instance of theNativeView created earlier(for example, NativeViewFactory.kt):Finally, register the platform view.You can do this in an app or a plugin.For app registration,modify the app’s main activity(for example, MainActivity.kt):For plugin registration,modify the plugin’s main class(for example, PlatformViewPlugin.kt):In your native code, implement the following:Extend io.flutter.plugin.platform.PlatformViewto provide a reference to the android.view.View(for example, NativeView.java):Create a factory class that creates aninstance of the NativeView created earlier(for example, NativeViewFactory.java):Finally, register the platform view.You can do this in an app or a plugin.For app registration,modify the app’s main activity(for example, MainActivity.java):For plugin registration,modify the plugin’s main file(for example, PlatformViewPlugin.java):For more information, see the API docs for:Finally, modify your build.gradle fileto require one of the minimal Android SDK versions:<topic_end><topic_start>Manual view invalidationCertain Android Views do not invalidate themselves when their content changes.Some example views include SurfaceView and SurfaceTexture.When your Platform View includes these views you are required tomanually invalidate the view after they have been drawn to(or more specifically: after the swap chain is flipped).Manual view invalidation is done by calling invalidate on the View or one of its parent views.<topic_end><topic_start>PerformancePlatform views in Flutter come with performance trade-offs.For example, in a typical Flutter app, the Flutter UI is composedon a dedicated raster thread. This allows Flutter apps to be fast,as the main platform thread is rarely blocked.While a platform view is rendered with hybrid composition,the Flutter UI is composed from the platform thread,which competes with other tasks like handling OS or plugin messages.Prior to Android 10, hybrid composition copied each Flutter frameout of the graphic memory into main memory, and then copied it backto a GPU texture. As this copy happens per frame, the performance ofthe entire Flutter UI might be impacted. In Android 10 or above, thegraphics memory is copied only once.Virtual display, on the other hand,makes each pixel of the native viewflow through additional intermediate graphic buffers,which cost graphic memory and drawing performance.For complex cases, there are some techniques thatcan be used to mitigate these issues.For example, you could use a placeholder texturewhile an animation is happening in Dart.In other words, if an animation is slow while aplatform view is rendered,then consider taking a screenshot of thenative view and rendering it as a texture.For more information, see:<topic_end><topic_start>Restore state on AndroidWhen a user runs a mobile app and then selects anotherapp to run, the first app is moved to the background,or backgrounded. The operating system (both iOS and Android)might kill the backgrounded app to release memory andimprove performance for the app running in the foreground.When the user selects the app again, bringing itback to the foreground, the OS relaunches it.But, unless you’ve set up a way to save thestate of the app before it was killed,you’ve lost the state and the app starts from scratch.The user has lost the continuity they expect,which is clearly not ideal.(Imagine filling out a lengthy form and being interruptedby a phone call before clicking Submit.)So, how can you restore the state of the app so thatit looks like it did before it was sent to thebackground?Flutter has a solution for this with theRestorationManager (and related classes)in the services library.With the RestorationManager, the Flutter frameworkprovides the state data to the engine as the statechanges, so that the app is ready when the OS signalsthat it’s about to kill the app, giving the app onlymoments to prepare.Instance state vs long-lived state  When should you use the RestorationManager and  when should you save state to long term storage?  Instance state  (also called short-term or ephemeral state),  includes unsubmitted form field values, the currently  selected tab, and so on. On Android, this is  limited to 1 MB and, if the app exceeds this,  it crashes with a TransactionTooLargeException  error in the native code.<topic_end><topic_start>OverviewYou can enable state restoration with just a few tasks:Define a restorationId or a restorationScopeIdfor all widgets that support it,such as TextField and ScrollView.This automatically enables built-in state restorationfor those widgets.For custom widgets,you must decide what state you want to restoreand hold that state in a RestorableProperty.(The Flutter API provides various subclasses fordifferent data types.)Define those RestorableProperty widgets in a State class that uses the RestorationMixin.Register those widgets with the mixin in arestoreState method.If you use any Navigator API (like push, pushNamed, and so on)migrate to the API that has “restorable” in the name(restorablePush, resstorablePushNamed, and so on)to restore the navigation stack.Other considerations:Providing a restorationId toMaterialApp, CupertinoApp, or WidgetsAppautomatically enables state restoration byinjecting a RootRestorationScope.If you need to restore state above the app class,inject a RootRestorationScope manually.The difference between a restorationId anda restorationScopeId: Widgets that take arestorationScopeID create a new restorationScope(a new RestorationBucket) into which all childrenstore their state. A restorationId means the widget(and its children) store the data in the surrounding bucket.<topic_end><topic_start>Restoring navigation stateIf you want your app to return to a particular routethat the user was most recently viewing(the shopping cart, for example), then you must implementrestoration state for navigation, as well.If you use the Navigator API directly,migrate the standard methods to restorablemethods (that have “restorable” in the name).For example, replace push with restorablePush.The VeggieSeasons example (listed under “Other resources” below)implements navigation with the go_router package.Setting the restorationIdvalues occur in the lib/screens classes.<topic_end><topic_start>Testing state restorationTo test state restoration, set up your mobile device so thatit doesn’t save state once an app is backgrounded.To learn how to do this for both iOS and Android,check out Testing state restoration on theRestorationManager page.warning Warning  Don’t forget to reenable  storing state on your device once you are  finished with testing!<topic_end><topic_start>Other resourcesFor further information on state restoration,check out the following resources:To learn more about short term and long term state,check out Differentiate between ephemeral stateand app state.You might want to check out packages on pub.dev thatperform state restoration, such as statePersistence.<topic_end><topic_start>Targeting ChromeOS with AndroidThis page discusses considerations unique to buildingAndroid apps that support ChromeOS with Flutter.<topic_end><topic_start>Flutter & ChromeOS tips & tricksFor the current versions of ChromeOS, only certain ports fromLinux are exposed to the rest of the environment.Here’s an example of how to launchFlutter DevTools for an Android app with portsthat will work:Then, navigate to http://127.0.0.1:8000/#in your Chrome browser and enter the URL to yourapplication. The last flutter run command youjust ran should output a URL similar to the formatof http://127.0.0.1:8080/auth_code=/. Use this URLand select “Connect” to start the Flutter DevToolsfor your Android app.<topic_end><topic_start>Flutter ChromeOS lint analysisFlutter has ChromeOS-specific lint analysis checksto make sure that the app that you’re buildingworks well on ChromeOS. It looks for thingslike required hardware in your Android Manifestthat aren’t available on ChromeOS devices,permissions that imply requests for unsupportedhardware, as well as other properties or codethat would bring a lesser experience on these devices.To activate these,you need to create a new analysis_options.yamlfile in your project folder to include these options.(If you have an existing analysis_options.yaml file,you can update it)To run these from the command line, use the following command:Sample output for this command might look like:<topic_end><topic_start>iOS<topic_end><topic_start>Topics<topic_end><topic_start>Add iOS devtools for FlutterTo choose the guide to add iOS devtools to your Flutter configuration,click the Getting Started path you followed.<topic_end><topic_start>Leveraging Apple's System APIs and FrameworksWhen you come from iOS development, you might need to findFlutter plugins that offer the same abilities as Apple’s systemlibraries. This might include accessing device hardware or interactingwith specific frameworks like HealthKit or MapKit.For an overview of how the SwiftUI framework compares to Flutter,see Flutter for SwiftUI developers.<topic_end><topic_start>Introducing Flutter pluginsDart calls libraries that contain platform-specific code plugins.When developing an app with Flutter, you use plugins to interactwith system libraries.In your Dart code, you use the plugin’s Dart API to call the nativecode from the system library being used. This means that you can writethe code to call the Dart API. The API then makes it work for allplatforms that the plugin supports.To learn more about plugins, see Using packages.Though this page links to some popular plugins,you can find thousands more, along with examples,on pub.dev. The following table does not endorse any particular plugin.If you can’t find a package that meets your need,you can create your own or use platform channels directly in your project.To learn more, see Writing platform-specific code.<topic_end><topic_start>Adding a plugin to your projectTo use an Apple framework within your native project,import it into your Swift or Objective-C file.To add a Flutter plugin, run flutter pub add package_namefrom the root of your project.This adds the dependency to your pubspec.yaml file.After you add the dependency, add an import statement for the packagein your Dart file.You might need to change app settings or initialization logic.If that’s needed, the package’s “Readme” page on pub.devshould provide details.<topic_end><topic_start>Flutter Plugins and Apple FrameworksSupports both Google Play Store on Android and Apple App Store on iOS. ↩Adds Google Pay payments on Android and Apple Pay payments on iOS. ↩Uses Firebase Cloud Messaging and integrates with APNs. ↩Includes sensors like accelerometer, gyroscope, etc. ↩Uses Google’s ML Kit and supports various features like text recognition, face detection, image labeling, landmark recognition, and barcode scanning. You can also create a custom model with Firebase. To learn more, see Use a custom TensorFlow Lite model with Flutter. ↩ ↩2Uses the OpenWeatherMap API. Other packages exist that can pull from different weather APIs. ↩<topic_end><topic_start>Adding a launch screen to your iOS appLaunch screens provide a simple initial experience while your iOS app loads.They set the stage for your application, while allowing time for the app engineto load and your app to initialize.All apps submitted to the Apple App Storemust provide a launch screenwith an Xcode storyboard.<topic_end><topic_start>Customize the launch screenThe default Flutter template includes an Xcodestoryboard named LaunchScreen.storyboardthat can be customized your own assets.By default, the storyboard displays a blank image,but you can change this. To do so,open the Flutter app’s Xcode projectby typing open ios/Runner.xcworkspacefrom the root of your app directory.Then select Runner/Assets.xcassetsfrom the Project Navigator anddrop in the desired images to the LaunchImage image set.Apple provides detailed guidance for launch screens aspart of the Human Interface Guidelines.<topic_end><topic_start>Adding an iOS App Clip targeterror Important  Targeting iOS 16 increases the uncompressed IPA payload size limit  to 15MB. Depending on the size of your app, you might hit the limit. (#71098).This guide describes how to manually add anotherFlutter-rendering iOS App Clip target to yourexisting Flutter project or add-to-app project.warning Warning  This is an advanced guide and is best intended  for audience with a working knowledge of iOS development.To see a working sample, see the App Clip sample on GitHub.<topic_end><topic_start>Step 1 - Open projectOpen your iOS Xcode project, such asios/Runner.xcworkspace for full-Flutter apps.<topic_end><topic_start>Step 2 - Add an App Clip target2.1Click on your project in the Project Navigator to showthe project settings.Press + at the bottom of the target list to add a new target.2.2Select the App Clip type for your new target.2.3Enter your new target detail in the dialog.Select Storyboard for Interface.Select the same language as your original target for Language.(In other words, to simplify the setup,don’t create a Swift App Clip target foran Objective-C main target, and vice versa.)2.4In the following dialog,activate the new scheme for the new target.2.5Back in the project settings, open the Build Phases tab.Drag Embedded App Clips to above Thin Binary.<topic_end><topic_start>Step 3 - Remove unneeded files3.1In the Project Navigator, in the newly created App Clip group,delete everything except Info.plist and<app clip target>.entitlements.lightbulb Tip  For add-to-app users, it’s up to the reader to decide  how much of this template to keep to invoke  FlutterViewController or FlutterEngine APIs  from this code later.Move files to trash.3.2If you don’t use the SceneDelegate.swift file,remove the reference to it in the Info.plist.Open the Info.plist file in the App Clip group.Delete the entire dictionary entry forApplication Scene Manifest.<topic_end><topic_start>Step 4 - Share build configurationsThis step isn’t necessary for add-to-app projectssince add-to-app projects have their custom buildconfigurations and versions.4.1Back in the project settings,select the project entry now rather than any targets.In the Info tab, under the Configurationsexpandable group, expand theDebug, Profile, and Release entries.For each, select the same value from the drop-down menufor the App Clip target as the entry selected for thenormal app target.This gives your App Clip target access to Flutter’srequired build settings.Set iOS Deployment Target to at least 16.0 to take advantage of the15MB size limit.4.2In the App Clip group’s Info.plist file, set:<topic_end><topic_start>Step 5 - Share code and assets<topic_end><topic_start>Option 1 - Share everythingAssuming the intent is to show the same Flutter UIin the standard app as in the App Clip,share the same code and assets.For each of the following: Main.storyboard, Assets.xcassets,LaunchScreen.storyboard, GeneratedPluginRegistrant.m, andAppDelegate.swift (and Supporting Files/main.m if using Objective-C),select the file, then in the first tab of the inspector,also include the App Clip target in the Target Membershipcheckbox group.<topic_end><topic_start>Option 2 - Customize Flutter launch for App ClipIn this case,do not delete everything listed in Step 3.Instead, use the scaffolding and the iOS add-to-app APIsto perform a custom launch of Flutter.For example to show a custom Flutter route.<topic_end><topic_start>Step 6 - Add App Clip associated domainsThis is a standard step for App Clip development.See the official Apple documentation.6.1Open the <app clip target>.entitlements file.Add an Associated Domains Array type.Add a row to the array with appclips:<your bundle id>.6.2The same associated domains entitlement needs to be addedto your main app, as well.Copy the <app clip target>.entitlements file from yourApp Clip group to your main app group and rename it tothe same name as your main targetsuch as Runner.entitlements.Open the file and delete theParent Application Identifiersentry for the main app’s entitlement file(leave that entry for the App Clip’s entitlement file).6.3Back in the project settings, select the main app’s target,open the Build Settings tab.Set the Code Signing Entitlements setting to therelative path of the second entitlements filecreated for the main app.<topic_end><topic_start>Step 7 - Integrate FlutterThese steps are not necessary for add-to-app.7.1For the Swift target,set the Objective-C Bridging Headerbuild setting to Runner/Runner-Bridging-Header.hIn other words,the same as the main app target’s build settings.7.2Now open the Build Phases tab. Press the + signand select New Run Script Phase.Drag that new phase to below the Dependencies phase.Expand the new phase and add this line to the script content:Uncheck Based on dependency analysis.In other words,the same as the main app target’s build phases.This ensures that your Flutter Dart code is compiledwhen running the App Clip target.7.3Press the + sign and select New Run Script Phase again.Leave it as the last phase.This time, add:Uncheck Based on dependency analysis.In other words,the same as the main app target’s build phases.This ensures that your Flutter app and engine are embeddedinto the App Clip bundle.<topic_end><topic_start>Step 8 - Integrate plugins8.1Open the Podfile for your Flutter projector add-to-app host project.For full-Flutter apps, replace the following section:with:At the top of the file,also uncomment platform :ios, '12.0' and set theversion to the lowest of the two target’s iOSDeployment Target.For add-to-app, add to:with:8.2From the command line,enter your Flutter project directoryand then install the pod:<topic_end><topic_start>RunYou can now run your App Clip target from Xcode byselecting your App Clip target from the scheme drop-down,selecting an iOS 16 or higher device and pressing run.To test launching an App Clip from the beginning,also consult Apple’s doc onTesting Your App Clip’s Launch Experience.<topic_end><topic_start>Debugging, hot reloadUnfortunately flutter attach cannot auto-discoverthe Flutter session in an App Clip due tonetworking permission restrictions.In order to debug your App Clip and use functionalitieslike hot reload, you must look for the Observatory URIfrom the console output in Xcode after running.You must then copy and paste it back into theflutter attach command to connect.For example:<topic_end><topic_start>Adding iOS app extensionsiOS app extensions allow you to expand functionalityoutside your app. Your app could appear as a home screen widget,or you can make portions of your app available within other apps.To learn more about app extensions, check outApple’s documentation.info Note  If you experience a build error when building an  iOS app that includes an app extension, be  aware that there is an open bug. The workaround  involves changing the order of the build process.  For more information, check out  Issue #9690 and Issue #135056.<topic_end><topic_start>How do you add an app extension to your Flutter app?To add an app extension to your Flutter app,add the extension point target to your Xcode project.In Xcode, select File -> New -> Target from the menu bar.To learn how to add a home screen widget to your iOS device,check out the Adding a Home Screen Widget to your Flutter appcodelab.<topic_end><topic_start>How do Flutter apps interact with App Extensions?Flutter apps interact with app extensions using the sametechniques as UIKit or SwiftUI apps.The containing app and the app extension don’t communicate directly.The containing app might not be running while the device userinteracts with the extension.The app and your extension can read and write toshared resources or use higher-level APIsto communicate with each other.<topic_end><topic_start>Using higher-level APIsSome extensions have APIs. For example, the Core Spotlight framework indexes your app,allowing users to search from Spotlight and Safari.The WidgetKit framework can trigger an updateof your home screen widget.To simplify how your app communicates with extensions,Flutter plugins wrap these APIs.To find plugins that wrap extension APIs,check out Leveraging Apple’s System APIs and Frameworksor search pub.dev.<topic_end><topic_start>Sharing resourcesTo share resources between your Flutter appand your app extension, put the Runner app targetand the extension target in the same App Group.info Note  You must be signed in to your Apple Developer account.To add a target to an App Group:Choose which App Group you want to add the target fromone of two options:When two targets belong to the same App Group,they can read from and write to the same source.Choose one of the following sources for your data.<topic_end><topic_start>Background updatesBackground tasks provide a means to update your extensionthrough code regardless of the status of your app.To schedule background work from your Flutter app,use the workmanager plugin.<topic_end><topic_start>Deep linkingYou might want to direct users from anapp extension to a specific page in your Flutter app.To open a specific route in your app,you can use Deep Linking.<topic_end><topic_start>Creating app extension UIs with FlutterSome app extensions display a user interface.For example, share extensions allow users to convenientlyshare content with other apps,such as sharing a picture to createa new post on a social media app.As of the 3.16 release, you can buildFlutter UI for an app extension,though you must use an extension-safeFlutter.xcframework and embed theFlutterViewController as described inthe following section.info Note  Due to the memory limitations of app extensions,  use Flutter to build an app extension UI for extension  types that have memory limits larger than 100MB.  For example, Share extensions have a 120MB memory limit.In addition, Flutter uses extra memory in debug mode.  Therefore, Flutter doesn’t fully support running  app extensions in debug mode on physical devices  when used to build extension UI; it might run out of memory.  As an alternative,  use an iOS simulator to test your extension in debug mode.Locate the extension-safe Flutter.xcframework file,at <path_to_flutter_sdk>/bin/cache/artifacts/engine/ios/extension_safe/Flutter.xcframework.Drag and drop the Flutter.xcframework file into yourshare extension’s frameworks and libraries list.Make sure the embed column says “Embed & Sign”.Open the Flutter app project settings in Xcodeto share build configurations.(Optional) Replace any storyboard files with an extension class, if needed.Embed the FlutterViewController as described inAdding a Flutter Screen. For example, you can display aspecific route in your Flutter app within a share extension.<topic_end><topic_start>Test extensionsTesting extensions on simulators and physical deviceshave slightly different procedures.<topic_end><topic_start>Test on a simulator<topic_end><topic_start>Test on a physical deviceYou can use the following procedure or theTesting on simulators instructionsto test on physical devices.<topic_end><topic_start>TutorialsFor step-by-step instruction for using appextensions with your Flutter iOS app, check out theAdding a Home Screen Widget to your Flutter appcodelab.<topic_end><topic_start>Binding to native iOS code using dart:ffiFlutter mobile and desktop apps can use thedart:ffi library to call native C APIs.FFI stands for foreign function interface.Other terms for similar functionality includenative interface and language bindings.info Note  This page describes using the dart:ffi library  in iOS apps. For information on Android, see  Binding to native Android code using dart:ffi.  For information in macOS, see  Binding to native macOS code using dart:ffi.  This feature is not yet supported for web plugins.Before your library or program can use the FFI libraryto bind to native code, you must ensure that thenative code is loaded and its symbols are visible to Dart.This page focuses on compiling, packaging,and loading iOS native code within a Flutter plugin or app.This tutorial demonstrates how to bundle C/C++sources in a Flutter plugin and bind to them usingthe Dart FFI library on iOS.In this walkthrough, you’ll create a C functionthat implements 32-bit addition and thenexposes it through a Dart plugin named “native_add”.<topic_end><topic_start>Dynamic vs static linkingA native library can be linked into an app eitherdynamically or statically. A statically linked libraryis embedded into the app’s executable image,and is loaded when the app starts.Symbols from a statically linked library can beloaded using DynamicLibrary.executable orDynamicLibrary.process.A dynamically linked library, by contrast, is distributedin a separate file or folder within the app,and loaded on-demand. On iOS, the dynamically linkedlibrary is distributed as a .framework folder.A dynamically linked library can be loaded intoDart using DynamicLibrary.open.API documentation is available from the Dart dev channel:Dart API reference documentation.<topic_end><topic_start>Create an FFI pluginTo create an FFI plugin called “native_add”,do the following:info Note  You can exclude platforms from --platforms that you don’t want  to build to. However, you need to include the platform of   the device you are testing on.This will create a plugin with C/C++ sources in native_add/src.These sources are built by the native build files in the variousos build folders.The FFI library can only bind against C symbols,so in C++ these symbols are marked extern "C".You should also add attributes to indicate that thesymbols are referenced from Dart,to prevent the linker from discarding the symbolsduring link-time optimization.__attribute__((visibility("default"))) __attribute__((used)).On iOS, the native_add/ios/native_add.podspec links the code.The native code is invoked from dart in lib/native_add_bindings_generated.dart.The bindings are generated with package:ffigen.<topic_end><topic_start>Other use cases<topic_end><topic_start>iOS and macOSDynamically linked libraries are automatically loaded bythe dynamic linker when the app starts. Their constituentsymbols can be resolved using DynamicLibrary.process.You can also get a handle to the library withDynamicLibrary.open to restrict the scope ofsymbol resolution, but it’s unclear how Apple’sreview process handles this.Symbols statically linked into the application binarycan be resolved using DynamicLibrary.executable orDynamicLibrary.process.<topic_end><topic_start>Platform libraryTo link against a platform library,use the following instructions:<topic_end><topic_start>First-party libraryA first-party native library can be included eitheras source or as a (signed) .framework file.It’s probably possible to include statically linkedarchives as well, but it requires testing.<topic_end><topic_start>Source codeTo link directly to source code,use the following instructions:Add the following prefix to theexported symbol declarations to ensure theyare visible to Dart:C/C++/Objective-CSwift<topic_end><topic_start>Compiled (dynamic) libraryTo link to a compiled dynamic library,use the following instructions:<topic_end><topic_start>Open-source third-party libraryTo create a Flutter plugin that includes bothC/C++/Objective-C and Dart code,use the following instructions:The native code is then statically linked intothe application binary of any app that usesthis plugin.<topic_end><topic_start>Closed-source third-party libraryTo create a Flutter plugin that includes Dartsource code, but distribute the C/C++ libraryin binary form, use the following instructions:warning WarningDo not upload this plugin  (or any plugin containing binary code) to pub.dev.  Instead, this plugin should be downloaded  from a trusted third-party,  as shown in the CocoaPods example.<topic_end><topic_start>Stripping iOS symbolsWhen creating a release archive (IPA),the symbols are stripped by Xcode.<topic_end><topic_start>Other ResourcesTo learn more about C interoperability, check out these videos:<topic_end><topic_start>Hosting native iOS views in your Flutter app with Platform ViewsPlatform views allow you to embed native views in a Flutter app,so you can apply transforms, clips, and opacity to the native viewfrom Dart.This allows you, for example, to use the nativeGoogle Maps from the Android and iOS SDKsdirectly inside your Flutter app.info Note  This page discusses how to host your own native iOS views  within a Flutter app.  If you’d like to embed native Android views  in your Flutter app,  see Hosting native Android views.iOS only uses Hybrid composition,which means that the nativeUIView is appended to the view hierarchy.To create a platform view on iOS,use the following instructions:<topic_end><topic_start>On the Dart sideOn the Dart side, create a Widgetand add the build implementation,as shown in the following steps.In the Dart widget file, make changes similar to those shown in native_view_example.dart:Add the following imports:<code_start>import 'package:flutter/foundation.dart';import 'package:flutter/services.dart';<code_end>Implement a build() method:<code_start>Widget build(BuildContext context) {  // This is used in the platform side to register the view.  const String viewType = '<platform-view-type>';  // Pass parameters to the platform side.  final Map<String, dynamic> creationParams = <String, dynamic>{};  return UiKitView(    viewType: viewType,    layoutDirection: TextDirection.ltr,    creationParams: creationParams,    creationParamsCodec: const StandardMessageCodec(),  );}<code_end>For more information, see the API docs for:UIKitView.<topic_end><topic_start>On the platform sideOn the platform side, use either Swift or Objective-C:Implement the factory and the platform view.The FLNativeViewFactory creates the platform view,and the platform view provides a reference to the UIView.For example, FLNativeView.swift:Finally, register the platform view.This can be done in an app or a plugin.For app registration,modify the App’s AppDelegate.swift:For plugin registration,modify the plugin’s main file(for example, FLPlugin.swift):In Objective-C, add the headers for the factory and the platform view.For example, as shown in FLNativeView.h:Implement the factory and the platform view.The FLNativeViewFactory creates the platform view,and the platform view provides a reference to theUIView. For example, FLNativeView.m:Finally, register the platform view.This can be done in an app or a plugin.For app registration,modify the App’s AppDelegate.m:For plugin registration,modify the main plugin file(for example, FLPlugin.m):For more information, see the API docs for:<topic_end><topic_start>Putting it togetherWhen implementing the build() method in Dart,you can use defaultTargetPlatformto detect the platform, and decide which widget to use:<code_start>Widget build(BuildContext context) {  // This is used in the platform side to register the view.  const String viewType = '<platform-view-type>';  // Pass parameters to the platform side.  final Map<String, dynamic> creationParams = <String, dynamic>{};  switch (defaultTargetPlatform) {    case TargetPlatform.android:    // return widget on Android.    case TargetPlatform.iOS:    // return widget on iOS.    default:      throw UnsupportedError('Unsupported platform view');  }}<code_end><topic_end><topic_start>PerformancePlatform views in Flutter come with performance trade-offs.For example, in a typical Flutter app, the Flutter UI is composed on a dedicated raster thread. This allows Flutter apps to be fast, as the main platform thread is rarely blocked.When a platform view is rendered with hybrid composition, the Flutter UI is composed from the platform thread. The platform thread competes with other tasks like handling OS or plugin messages.When an iOS PlatformView is on screen, the screen refresh rate is capped at 80fps to avoid rendering janks.For complex cases, there are some techniques that can be used to mitigate performance issues.For example, you could use a placeholder texture while an animation is happening in Dart. In other words, if an animation is slow while a platform view is rendered, then consider taking a screenshot of the native view and rendering it as a texture.<topic_end><topic_start>Composition limitationsThere are some limitations when composing iOS Platform Views.<topic_end><topic_start>iOS debuggingDue to security aroundlocal network permissions in iOS 14 or later,you must accept a permission dialog box to enableFlutter debugging functionalities such as hot-reloadand DevTools.This affects debug and profile builds only and won’tappear in release builds. You can also allow thispermission by enablingSettings > Privacy > Local Network > Your App.<topic_end><topic_start>Restore state on iOSWhen a user runs a mobile app and then selects anotherapp to run, the first app is moved to the background,or backgrounded. The operating system (both iOS and Android)often kill the backgrounded app to release memory orimprove performance for the app running in the foreground.You can use the RestorationManager (and related)classes to handle state restoration. An iOS app requires a bit of extra setup in Xcode,but the restoration classes otherwise work the same onboth iOS and Android.For more information, check out State restoration on Androidand the VeggieSeasons code sample.<topic_end><topic_start>Linux<topic_end><topic_start>Topics<topic_end><topic_start>Add Linux devtools for FlutterTo choose the guide to add Linux devtools to your Flutter configuration,click the Getting Started path you followed.<topic_end><topic_start>Building Linux apps with FlutterThis page discusses considerations unique to buildingLinux apps with Flutter, including shell integrationand preparation of apps for distribution.<topic_end><topic_start>Integrating with LinuxThe Linux programming interface,comprising library functions and system calls,is designed around the C language and ABI.Fortunately, Dart provides dart:ffi,which is designed to enable Dart programsto efficiently call into C libraries.FFI provides Flutter apps with the ability toallocate native memory with malloc or calloc,support for pointers, structs and callbacks,and ABI types like long and size_t.For more information about calling C librariesfrom Flutter, see C interop using dart:ffi.Many apps will benefit from using a package thatwraps the underlying librarycalls in a more convenient, idiomatic Dart API.Canonical has built a series of packageswith a focus on enabling Dart and Flutter on Linux,including support for desktop notifications,dbus, network management, and Bluetooth.More generally, many other packages support Linux,including common packages such as url_launcher,shared_preferences, file_selector, andpath_provider.<topic_end><topic_start>Preparing Linux apps for distributionThe executable binary can be found in your project underbuild/linux/<build mode>/bundle/. Alongside yourexecutable binary in the bundle directory there aretwo directories:In addition to these files, your application alsorelies on various operating system libraries thatit’s been compiled against.You can see the full list by running lddagainst your application. For example,assuming you have a Flutter desktop applicationcalled linux_desktop_test, you could inspectthe system libraries it depends upon as follows:To wrap up this application for distributionyou need to include everything in the bundle directory,and make sure the Linux system you are installingit on has all of the system libraries required.This could be as simple as:For information on publishing a Linux applicationto the Snap Store, seeBuild and release a Linux application to the Snap Store.<topic_end><topic_start>macOS<topic_end><topic_start>Topics<topic_end><topic_start>Add macOS devtools for FlutterTo choose the guide to add macOS devtools to your Flutter configuration,click the Getting Started path you followed.<topic_end><topic_start>Building macOS apps with FlutterThis page discusses considerations unique to buildingmacOS apps with Flutter, including shell integrationand distribution of macOS apps through the Apple Store.<topic_end><topic_start>Integrating with macOS look and feelWhile you can use any visual style or theme you chooseto build a macOS app, you might want to adapt your appto more fully align with the macOS look and feel.Flutter includes the Cupertino widget set,which provides a set of widgets forthe current iOS design language.Many of these widgets, including sliders,switches and segmented controls,are also appropriate for use on macOS.Alternatively, you might find the macos_uipackage a good fit for your needs.This package provides widgets and themes thatimplement the macOS design language,including a MacosWindow frame and scaffold,toolbars, pulldown andpop-up buttons, and modal dialogs.<topic_end><topic_start>Building macOS appsTo distribute your macOS application, you can eitherdistribute it through the macOS App Store,or you can distribute the .app itself,perhaps from your own website.As of macOS 10.14.5, you need to notarizeyour macOS application before distributingit outside of the macOS App Store.The first step in both of the above processesinvolves working with your application inside of Xcode.To be able to compile your application from inside ofXcode you first need to build the application for releaseusing the flutter build command, then open theFlutter macOS Runner application.Once inside of Xcode, follow either Apple’sdocumentation on notarizing macOS Applications, oron distributing an application through the App Store.You should also read through themacOS-specific supportsection below to understand how entitlements,the App Sandbox, and the Hardened Runtimeimpact your distributable application.Build and release a macOS app provides a more detailedstep-by-step walkthrough of releasing a Flutter app to theApp Store.<topic_end><topic_start>Entitlements and the App SandboxmacOS builds are configured by default to be signed,and sandboxed with App Sandbox.This means that if you want to confer specificcapabilities or services on your macOS app,such as the following:Then you must set up specific entitlements in Xcode.The following section tells you how to do this.<topic_end><topic_start>Setting up entitlementsManaging sandbox settings is done in themacos/Runner/*.entitlements files. When editingthese files, you shouldn’t remove the originalRunner-DebugProfile.entitlements exceptions(that support incoming network connections and JIT),as they’re necessary for the debug and profilemodes to function correctly.If you’re used to managing entitlement files throughthe Xcode capabilities UI, be aware that the capabilitieseditor updates only one of the two files or,in some cases, it creates a whole new entitlementsfile and switches the project to use it for all configurations.Either scenario causes issues. We recommend that youedit the files directly. Unless you have a very specificreason, you should always make identical changes to both files.If you keep the App Sandbox enabled (which is required if youplan to distribute your application in the App Store),you need to manage entitlements for your applicationwhen you add certain plugins or other native functionality.For instance, using the file_chooser pluginrequires adding either thecom.apple.security.files.user-selected.read-only orcom.apple.security.files.user-selected.read-write entitlement.Another common entitlement iscom.apple.security.network.client,which you must add if you make any network requests.Without the com.apple.security.network.client entitlement,for example, network requests fail with a message such as:Important: The com.apple.security.network.server  entitlement, which allows incoming network connections,  is enabled by default only for debug and profile  builds to enable communications between Flutter tools  and a running app. If you need to allow incoming  network requests in your application,  you must add the com.apple.security.network.server  entitlement to Runner-Release.entitlements as well,  otherwise your application will work correctly for debug or  profile testing, but will fail with release builds.For more information on these topics,see App Sandbox and Entitlementson the Apple Developer site.<topic_end><topic_start>Hardened RuntimeIf you choose to distribute your application outsideof the App Store, you need to notarize your applicationfor compatibility with macOS 10.15+.This requires enabling the Hardened Runtime option.Once you have enabled it, you need a valid signingcertificate in order to build.By default, the entitlements file allows JIT fordebug builds but, as with App Sandbox, you mightneed to manage other entitlements.If you have both App Sandbox and HardenedRuntime enabled, you might need to add multipleentitlements for the same resource.For instance, microphone access would require bothcom.apple.security.device.audio-input (for Hardened Runtime)and com.apple.security.device.microphone (for App Sandbox).For more information on this topic,see Hardened Runtime on the Apple Developer site.<topic_end><topic_start>Binding to native macOS code using dart:ffiFlutter mobile and desktop apps can use thedart:ffi library to call native C APIs.FFI stands for foreign function interface.Other terms for similar functionality includenative interface and language bindings.info Note  This page describes using the dart:ffi library  in macOS desktop apps.  For information on Android, see  Binding to native Android code using dart:ffi.  For information on iOS, see  Binding to native iOS code using dart:ffi.  This feature is not yet supported for web plugins.Before your library or program can use the FFI libraryto bind to native code, you must ensure that thenative code is loaded and its symbols are visible to Dart.This page focuses on compiling, packaging,and loading macOS native code within a Flutter plugin or app.This tutorial demonstrates how to bundle C/C++sources in a Flutter plugin and bind to them usingthe Dart FFI library on macOS.In this walkthrough, you’ll create a C functionthat implements 32-bit addition and thenexposes it through a Dart plugin named “native_add”.<topic_end><topic_start>Dynamic vs static linkingA native library can be linked into an app eitherdynamically or statically. A statically linked libraryis embedded into the app’s executable image,and is loaded when the app starts.Symbols from a statically linked library can beloaded using DynamicLibrary.executable orDynamicLibrary.process.A dynamically linked library, by contrast, is distributedin a separate file or folder within the app,and loaded on-demand. On macOS, the dynamically linkedlibrary is distributed as a .framework folder.A dynamically linked library can be loaded intoDart using DynamicLibrary.open.API documentation is available from the Dart dev channel:Dart API reference documentation.<topic_end><topic_start>Create an FFI pluginIf you already have a plugin, skip this step.To create a plugin called “native_add”,do the following:info Note  You can exclude platforms from --platforms that you don’t want  to build to. However, you need to include the platform of   the device you are testing on.This will create a plugin with C/C++ sources in native_add/src.These sources are built by the native build files in the variousos build folders.The FFI library can only bind against C symbols,so in C++ these symbols are marked extern "C".You should also add attributes to indicate that thesymbols are referenced from Dart,to prevent the linker from discarding the symbolsduring link-time optimization.__attribute__((visibility("default"))) __attribute__((used)).On iOS, the native_add/macos/native_add.podspec links the code.The native code is invoked from dart in lib/native_add_bindings_generated.dart.The bindings are generated with package:ffigen.<topic_end><topic_start>Other use cases<topic_end><topic_start>iOS and macOSDynamically linked libraries are automatically loaded bythe dynamic linker when the app starts. Their constituentsymbols can be resolved using DynamicLibrary.process.You can also get a handle to the library withDynamicLibrary.open to restrict the scope ofsymbol resolution, but it’s unclear how Apple’sreview process handles this.Symbols statically linked into the application binarycan be resolved using DynamicLibrary.executable orDynamicLibrary.process.<topic_end><topic_start>Platform libraryTo link against a platform library,use the following instructions:<topic_end><topic_start>First-party libraryA first-party native library can be included eitheras source or as a (signed) .framework file.It’s probably possible to include statically linkedarchives as well, but it requires testing.<topic_end><topic_start>Source codeTo link directly to source code,use the following instructions:Add the following prefix to theexported symbol declarations to ensure theyare visible to Dart:C/C++/Objective-CSwift<topic_end><topic_start>Compiled (dynamic) libraryTo link to a compiled dynamic library,use the following instructions:<topic_end><topic_start>Compiled (dynamic) library (macOS)To add a closed source library to aFlutter macOS Desktop app,use the following instructions:<topic_end><topic_start>Other ResourcesTo learn more about C interoperability, check out these videos:<topic_end><topic_start>Web support for FlutterFlutter’s web support delivers the same experiences on the web as on mobile.Building on the portability of Dart, the power of the web platform and theflexibility of the Flutter framework, you can now build apps for iOS, Android,and the browser from the same codebase. You can compile existing Flutter codewritten in Dart into a web experience because it is exactly the same Flutterframework and web is just another device target for your app.Adding web support to Flutter involved implementing Flutter’score drawing layer on top of standard browser APIs, in additionto compiling Dart to JavaScript, instead of the ARM machine code thatis used for mobile applications. Using a combination of DOM, Canvas,and WebAssembly, Flutter can provide a portable, high-quality,and performant user experience across modern browsers.We implemented the core drawing layer completely in Dartand used Dart’s optimized JavaScript compiler to compile theFlutter core and framework along with your applicationinto a single, minified source file that can be deployed toany web server.While you can do a lot on the web,Flutter’s web support is most valuable in thefollowing scenarios:Not every HTML scenario is ideally suited for Flutter at this time.For example, text-rich, flow-based, static content such as blog articlesbenefit from the document-centric model that the web is built around,rather than the app-centric services that a UI framework like Fluttercan deliver. However, you can use Flutter to embed interactiveexperiences into these websites.For a glimpse into how to migrate your mobile app to web, seethe following video:<topic_end><topic_start>ResourcesThe following resources can help you get started:<topic_end><topic_start>Add Web devtools for FlutterTo choose the guide to add Web devtools to your Flutter configuration,click the Getting Started path you followed.<topic_end><topic_start>Building a web application with FlutterThis page covers the following steps for getting started with web support:<topic_end><topic_start>RequirementsTo create a Flutter app with web support,you need the following software:For more information, see the web FAQ.<topic_end><topic_start>Create a new project with web supportYou can use the following stepsto create a new project with web support.<topic_end><topic_start>Set upRun the following commands to use the latest version of the Flutter SDK:warning Warning  Running flutter channel stable replaces your current version of Flutter  with the stable version and can take time if your connection is slow.  After this, running flutter upgrade upgrades your install to the latest stable.  Returning to another channel (beta or master) requires calling flutter channel <channel> explicitly.If Chrome is installed,the flutter devices command outputs a Chrome devicethat opens the Chrome browser with your app running,and a Web Server that provides the URL serving the app.In your IDE, you should see Chrome (web) in the device pulldown.<topic_end><topic_start>Create and runCreating a new project with web support is no differentthan creating a new Flutter project for other platforms.<topic_end><topic_start>IDECreate a new app in your IDE and it automaticallycreates iOS, Android, desktop, and web versions of your app.From the device pulldown, select Chrome (web)and run your app to see it launch in Chrome.<topic_end><topic_start>Command lineTo create a new app that includes web support(in addition to mobile support), run the following commands,substituting my_app with the name of your project:To serve your app from localhost in Chrome,enter the following from the top of the package:info Note  If there aren’t any other connected devices,  the -d chrome is optional.The flutter run command launches the application using thedevelopment compiler in a Chrome browser.warning WarningHot reload is not supported in a web browser  Currently, Flutter supports hot restart,  but not hot reload in a web browser.<topic_end><topic_start>BuildRun the following command to generate a release build:If you receive a not supported error, run the following command:A release build uses dart2js(instead of the development compiler)to produce a single JavaScript file main.dart.js.You can create a release build using release mode(flutter run --release) or by using flutter build web.This populates a build/web directorywith built files, including an assets directory,which need to be served together.You can also include --web-renderer html  or --web-renderer canvaskit toselect between the HTML or CanvasKit renderers, respectively. For moreinformation, see Web renderers.To learn more, seeBuild and release a web app.<topic_end><topic_start>Add web support to an existing appTo add web support to an existing projectcreated using a previous version of Flutter,run the following commandfrom your project’s top-level directory:If you receive a not supported error, run the following command:<topic_end><topic_start>Web FAQ<topic_end><topic_start>What scenarios are ideal for Flutter on the web?Not every web page makes sense in Flutter, but we think Flutter is particularlysuited for app-centric experiences:At this time, Flutter is not suitable for static websites with text-richflow-based content. For example, blog articles benefit from the document-centricmodel that the web is built around, rather than the app-centric services that aUI framework like Flutter can deliver. However, you can use Flutter to embedinteractive experiences into these websites.For more information on how you can use Flutter on the web,see Web support for Flutter.<topic_end><topic_start>Search Engine Optimization (SEO)In general, Flutter is geared towards dynamic application experiences. Flutter’sweb support is no exception. Flutter web prioritizes performance, fidelity, andconsistency. This means application output does not align with what searchengines need to properly index. For web content that is static or document-like,we recommend using HTML—just like we do on flutter.dev,dart.dev, and pub.dev. You should alsoconsider separating your primary application experience—created in Flutter—fromyour landing page, marketing content, and help content—created usingsearch-engine optimized HTML.<topic_end><topic_start>How do I create an app that also runs on the web?See building a web app with Flutter.<topic_end><topic_start>Does hot reload work with a web app?No, but you can use hot restart. Hot restart is a fast way of seeing yourchanges without having to relaunch your web app and wait for it to compile andload. This works similarly to the hot reload feature for Flutter mobiledevelopment. The only difference is that hot reload remembers your state and hotrestart doesn’t.<topic_end><topic_start>How do I restart the app running in the browser?You can either use the browser’s refresh button,or you can enter “R” in the console where“flutter run -d chrome” is running.<topic_end><topic_start>Which web browsers are supported by Flutter?Flutter web apps can run on the following browsers:During development, Chrome (on macOS, Windows, and Linux) and Edge (on Windows)are supported as the default browsers for debugging your app.<topic_end><topic_start>Can I build, run, and deploy web apps in any of the IDEs?You can select Chrome or Edge as the target device inAndroid Studio/IntelliJ and VS Code.The device pulldown should now include the Chrome (web)option for all channels.<topic_end><topic_start>How do I build a responsive app for the web?See Creating responsive apps.<topic_end><topic_start>Can I use dart:io with a web app?No. The file system is not accessible from the browser.For network functionality, use the httppackage. Note that security works somewhatdifferently because the browser (and not the app)controls the headers on an HTTP request.<topic_end><topic_start>How do I handle web-specific imports?Some plugins require platform-specific imports, particularly if they use thefile system, which is not accessible from the browser. To use these pluginsin your app, see the documentation for conditional importson dart.dev.<topic_end><topic_start>Does Flutter web support concurrency?Dart’s concurrency support via isolatesis not currently supported in Flutter web.Flutter web apps can potentially work around thisby using web workers,although no such support is built in.<topic_end><topic_start>How do I embed a Flutter web app in a web page?You can embed a Flutter web app,as you would embed other content,in an iframe tag of an HTML file.In the following example, replace “URL”with the location of your hosted HTML page:If you encounter problems, please file an issue.<topic_end><topic_start>How do I debug a web app?Use Flutter DevTools for the following tasks:Use Chrome DevTools for the following tasks:<topic_end><topic_start>How do I test a web app?Use widget tests or integration tests. To learn more aboutrunning integration tests in a browser, see the Integration testing page.<topic_end><topic_start>How do I deploy a web app?See Preparing a web app for release.<topic_end><topic_start>Does Platform.is work on the web?Not currently.<topic_end><topic_start>Web renderersWhen running and building apps for the web, you can choose between two differentrenderers. This page describes both renderers and how to choose the best one foryour needs. The two renderers are:<topic_end><topic_start>Command line optionsThe --web-renderer command line option takes one of three values, auto,html, or canvaskit.This flag can be used with the run or build subcommands. For example:This flag is ignored when a non-browser (mobile or desktop) devicetarget is selected.<topic_end><topic_start>Runtime configurationTo override the web renderer at runtime:The web renderer can’t be changed after the Flutter engine startup processbegins in main.dart.js.info Note  As of Flutter 3.7.0,  setting a window.flutterWebRenderer  (an approach used in previous releases) displays a  deprecation notice in the JS console. For more information,  check out Customizing web app initialization.<topic_end><topic_start>Choosing which option to useChoose the auto option (default) if you are optimizing for download size onmobile browsers and optimizing for performance on desktop browsers.Choose the html option if you are optimizing download size over performance onboth desktop and mobile browsers.Choose the canvaskit option if you are prioritizing performance andpixel-perfect consistency on both desktop and mobile browsers.<topic_end><topic_start>ExamplesRun in Chrome using the default renderer option (auto):Build your app in release mode, using the default (auto) option:Build your app in release mode, using just the CanvasKit renderer:Run  your app in profile mode using the HTML renderer:<topic_end><topic_start>Customizing web app initializationYou can customize how a Flutter app is initialized on the webusing the _flutter.loader JavaScript API provided by flutter.js.This API can be used to display a loading indicator in CSS,prevent the app from loading based on a condition,or wait until the user presses a button before showing the app.The initialization process is split into the following stages:This page shows how to customize the behaviorat each stage of the initialization process.<topic_end><topic_start>Getting startedBy default, the index.html filegenerated by the flutter create commandcontains a script tagthat calls loadEntrypoint from the flutter.js file:info Note  In Flutter 2.10 or earlier,  this script doesn’t support customization.  To upgrade your index.html file to the latest version,  see Upgrading an older project.The loadEntrypoint function calls the onEntrypointLoaded callbackonce the Service Worker is initialized, and the main.dart.js entrypointhas been downloaded and run by the browser. Flutter also callsonEntrypointLoaded on every hot restart during development.The onEntrypointLoaded callback receives an engine initializer object asits only parameter. Use the engine initializer to set the run-timeconfiguration, and start the Flutter Web engine.The initializeEngine() function returns a Promisethat resolves with an app runner object. The app runner has asingle method, runApp(), that runs the Flutter app.<topic_end><topic_start>Customizing web app initializationIn this section,learn how to customize each stage of your app’s initialization.<topic_end><topic_start>Loading the entrypointThe loadEntrypoint method accepts these parameters:The serviceWorker JavaScript object accepts the following properties:<topic_end><topic_start>Initializing the engineAs of Flutter 3.7.0, you can use the initializeEngine method toconfigure several run-time options of the Flutter web engine through aplain JavaScript object.You can add any of the following optional parameters:info Note  Some of the parameters described above might have been overridden  in previous releases by using properties in the window object.  That approach is still supported, but displays a deprecation  notice in the JS console, as of Flutter 3.7.0.<topic_end><topic_start>Engine configuration exampleThe initializeEngine method lets you pass any of the configurationparameters described above to your Flutter app.Consider the following example.Your Flutter app should target an HTML element with id="flutter_app" anduse the canvaskit renderer. The resulting JavaScript code would resemblethe following:For a more detailed explanation of each parameter, take a look at the“Runtime parameters” documentation section of the configuration.dartfile of the web engine.<topic_end><topic_start>Skipping this stepInstead of calling initializeEngine() on the engine initializer (and thenrunApp() on the application runner), you can call autoStart() toinitialize the engine with its default configuration, and then start the appimmediately after the initialization is complete:<topic_end><topic_start>Example: Display a progress indicatorTo give the user of your application feedbackduring the initialization process,use the hooks provided for each stage to update the DOM:For a more practical example using CSS animations,see the initialization code for the Flutter Gallery.<topic_end><topic_start>Upgrading an older projectIf your project was created in Flutter 2.10 or earlier,you can create a new index.html filewith the latest initialization template by runningflutter create as follows.First, remove the files from your /web directory.Then, from your project directory, run the following:<topic_end><topic_start>Display images on the webThe web supports the standard Image widget to display images.However, because web browsers are built to run untrusted code safely,there are certain limitations in what you can do with images comparedto mobile and desktop platforms. This page explains these limitationsand offers ways to work around them.<topic_end><topic_start>BackgroundThis section summarizes the technologies availableacross Flutter and the web,on which the solutions below are based on.<topic_end><topic_start>Images in FlutterFlutter offers the Image widget as well as the low-leveldart:ui/Image class for rendering images.The Image widget has enough functionality for most use-cases.The dart:ui/Image class can be used inadvanced situations where fine-grained controlof the image is needed.<topic_end><topic_start>Images on the webThe web offers several methods for displaying images.Below are some of the common ones:Each option has its own benefits and drawbacks.For example, the built-in elements fit nicely amongother HTML elements, and they automatically takeadvantage of browser caching, and built-in imageoptimization and memory management.They allow you to safely display images from arbitrary sources(more on than in the CORS section below).drawImage is great when the image must fit withinother content rendered using the <canvas> element.You also gain control over image sizing and,when the CORS policy allows it, read the pixelsof the image back for further processing.Finally, WebGL gives you the highest degree ofcontrol over the image. Not only can you read the pixels andapply custom image algorithms, but you can also use GLSL forhardware-acceleration.<topic_end><topic_start>Cross-Origin Resource Sharing (CORS)CORS is a mechanism that browsers use to controlhow one site accesses the resources of another site.It is designed such that, by default, one web-siteis not allowed to make HTTP requests to another siteusing XHR or fetch.This prevents scripts on another site from acting on behalfof the user and from gaining access to anothersite’s resources without permission.When using <img>, <picture>, or <canvas>,the browser automatically blocks access to pixelswhen it knows that an image is coming from another siteand the CORS policy disallows access to data.WebGL requires access to the image data in orderto be able to render the image. Therefore,images to be rendered using WebGL must only come from serversthat have a CORS policy configured to work withthe domain that serves your application.<topic_end><topic_start>Flutter renderers on the webFlutter offers a choice of two renderers on the web:Because the HTML renderer uses the <img>element it can display images fromarbitrary sources. However,this places the following limitations on what youcan do with them:The CanvasKit renderer implements Flutter’s image API fully.However, it requires access to image pixels to do so,and is therefore subject to the CORS policy.<topic_end><topic_start>Solutions<topic_end><topic_start>In-memory, asset, and same-origin network imagesIf the app has the bytes of the encoded image in memory,provided as an asset, or stored on thesame server that serves the application(also known as same-origin), no extra effort is necessary.The image can be displayed usingImage.memory, Image.asset, and Image.networkin both HTML and CanvasKit modes.<topic_end><topic_start>Cross-origin imagesThe HTML renderer can load cross-origin imageswithout extra configuration.CanvasKit requires that the app gets the bytes of the encoded image.There are several ways to do this, discussed below.<topic_end><topic_start>Host your images in a CORS-enabled CDN.Typically, content delivery networks (CDN)can be configured to customize what domainsare allowed to access your content.For example, Firebase site hosting allowsspecifying a custom Access-Control-Allow-Originheader in the firebase.json file.<topic_end><topic_start>Lack control over the image server? Use a CORS proxy.If the image server cannot be configured to allow CORSrequests from your application,you might still be able to load images by proxyingthe requests through another server. This requires that theintermediate server has sufficient access to load the images.This method can be used in situations when the originalimage server serves images publicly,but is not configured with the correct CORS headers.Examples:<topic_end><topic_start>Use <img> in a platform view.Flutter supports embedding HTML inside the app usingHtmlElementView.  Use it to create an <img>element to render the image from another domain.However, do keep in mind that this comes with thelimitations explained in Flutter renderers on the web.<topic_end><topic_start>Windows<topic_end><topic_start>Topics<topic_end><topic_start>Add Windows devtools for FlutterTo choose the guide to add Visual Studio to your Flutter configuration,click the Getting Started path you followed.<topic_end><topic_start>Building Windows apps with FlutterThis page discusses considerations unique to buildingWindows apps with Flutter, including shell integrationand distribution of Windows apps through theMicrosoft Store on Windows.<topic_end><topic_start>Integrating with WindowsThe Windows programming interface combines traditional Win32 APIs,COM interfaces and more modern Windows Runtime libraries.As all these provide a C-based ABI,you can call into the services provided by the operatingsystem using Dart’s Foreign Function Interface library (dart:ffi).FFI is designed to enable Dart programs to efficiently call intoC libraries. It provides Flutter apps with the ability to allocatenative memory with malloc or calloc, support for pointers,structs and callbacks, and ABI types like long and size_t.For more information about calling C libraries from Flutter,see C interop using dart:ffi.In practice, while it is relatively straightforward to callbasic Win32 APIs from Dart in this way,it is easier to use a wrapper library that abstracts theintricacies of the COM programming model.The win32 package provides a libraryfor accessing thousands of common Windows APIs,using metadata provided by Microsoft for consistency and correctness.The package also includes examples ofa variety of common use cases,such as WMI, disk management, shell integration,and system dialogs.A number of other packages build on this foundation,providing idiomatic Dart access for the Windows registry,gamepad support, biometric storage,taskbar integration, and serial port access, to name a few.More generally, many other packages support Windows,including common packages such as url_launcher, shared_preferences, file_selector, and path_provider.<topic_end><topic_start>Supporting Windows UI guidelinesWhile you can use any visual style or theme you choose,including Material, some app authors might wish to buildan app that matches the conventions of Microsoft’sFluent design system. The fluent_ui package,a Flutter Favorite, provides support for visualsand common controls that are commonly found inmodern Windows apps, including navigation views,content dialogs, flyouts, datepickers, and tree view widgets.In addition, Microsoft offers fluentui_system_icons,a package that provides easy access to thousands ofFluent icons for use in your Flutter app.Lastly, the bitsdojo_window package provides supportfor “owner draw” title bars, allowing you to replacethe standard Windows title bar with a custom onethat matches the rest of your app.<topic_end><topic_start>Customizing the Windows host applicationWhen you create a Windows app, Flutter generates asmall C++ application that hosts Flutter.This “runner app” is responsible for creating and sizing atraditional Win32 window, initializing the Flutterengine and any native plugins,and running the Windows message loop(passing relevant messages on to Flutter for further processing).You can, of course, make changes to this code to suit your needs,including modifying the app name and icon,and setting the window’s initial size and location.The relevant code is in main.cpp,where you will find code similar to the following:Replace myapp with the title you would like displayed in theWindows caption bar, as well as optionally adjusting thedimensions for size and the window coordinates.To change the Windows application icon, replace theapp_icon.ico file in the windows\runner\resourcesdirectory with an icon of your preference.The generated Windows executable filename can be changedby editing the BINARY_NAME variable in windows/CMakeLists.txt:When you run flutter build windows,the executable file generated in thebuild\windows\runner\Release directorywill match the newly given name.Finally, further properties for the app executableitself can be found in the Runner.rc file in thewindows\runner directory. Here you can change thecopyright information and application version thatis embedded in the Windows app, which is displayedin the Windows Explorer properties dialog box.To change the version number, edit the VERSION_AS_NUMBERand VERSION_AS_STRING properties;other information can be edited in the StringFileInfo block.<topic_end><topic_start>Compiling with Visual StudioFor most apps, it’s sufficient to allow Flutter tohandle the compilation process using the flutter runand flutter build commands. If you are making significantchanges to the runner app or integrating Flutter into an existing app,you might want to load or compile the Flutter app in Visual Studio itself.Follow these steps:Run flutter build windows to create the build\ directory.Open the Visual Studio solution file for the Windows runner,which can now be found in the build\windows directory,named according to the parent Flutter app.In Solution Explorer, you will see a number of projects.Right-click the one that has the same name as the Flutter app,and choose Set as Startup Project.To generate the necessary dependencies,run Build > Build SolutionYou can also press/Ctrl + Shift + B.To run the Windows app from Visual Studio, go to Debug > Start Debugging.You can also press F5.Use the toolbar to switch between Debug and Releaseconfigurations as appropriate.<topic_end><topic_start>Distributing Windows appsThere are various approaches you can use fordistributing your Windows application.Here are some options:<topic_end><topic_start>MSIX packagingMSIX, the new Windows application package format,provides a modern packaging format and installer.This format can either be used to ship applicationsto the Microsoft Store on Windows, or you candistribute app installers directly.The easiest way to create an MSIX distributionfor a Flutter project is to use themsix pub package.For an example of using the msix packagefrom a Flutter desktop app,see the Desktop Photo Search sample.<topic_end><topic_start>Create a self-signed .pfx certificate for local testingFor private deployment and testing with the helpof the MSIX installer, you need to give your application adigital signature in the form of a .pfx certificate.For deployment through the Windows Store,generating a .pfx certificate is not required.The Windows Store handles creation and managementof certificates for applicationsdistributed through its store.Distributing your application by self hosting it on awebsite requires a certificate signed by aCertificate Authority known to Windows.Use the following instructions to generate aself-signed .pfx certificate.<topic_end><topic_start>Building your own zip file for WindowsThe Flutter executable, .exe, can be found in yourproject under build\windows\runner\<build mode>\.In addition to that executable, you need the following:Place the DLL files in the directory next to the executableand the other DLLs, and bundle them together in a zip file.The resulting structure looks something like this:At this point if desired it would be relatively simple toadd this folder to a Windows installer such as Inno Setup, WiX, etc.<topic_end><topic_start>Using packagesFlutter supports using shared packages contributed by other developersto the Flutter and Dart ecosystems. This allows quickly buildingan app without having to develop everything from scratch.What is the difference between a package  and a plugin? A plugin is a type of  package—the full designation is plugin package,  which is generally shortened to plugin.Existing packages enable many use cases—for example,making network requests (http),navigation/route handling (go_router),integration with device APIs(url_launcher and battery_plus),and using third-party platform SDKs like Firebase(FlutterFire).To write a new package, see developing packages.To add assets, images, or fonts,whether stored in files or packages,see Adding assets and images.<topic_end><topic_start>Using packagesThe following section describes how to useexisting published packages.<topic_end><topic_start>Searching for packagesPackages are published to pub.dev.The Flutter landing page on pub.dev displaystop packages that are compatible with Flutter(those that declare dependencies generally compatible with Flutter),and supports searching among all published packages.The Flutter Favorites page on pub.dev liststhe plugins and packages that have been identified aspackages you should first consider using when writingyour app. For more information on what it means tobe a Flutter Favorite, see theFlutter Favorites program.You can also browse the packages on pub.dev by filteringon Android, iOS, web,Linux, Windows, macOS,or any combination thereof.<topic_end><topic_start>Adding a package dependency to an appTo add the package, css_colors, to an app:<topic_end><topic_start>Adding a package dependency to an app using flutter pub addTo add the package, css_colors, to an app:<topic_end><topic_start>Removing a package dependency to an app using flutter pub removeTo remove the package, css_colors, to an app:The Installing tab,available on any package page on pub.dev,is a handy reference for these steps.For a complete example,see the css_colors example below.<topic_end><topic_start>Conflict resolutionSuppose you want to use some_package andanother_package in an app,and both of these depend on url_launcher,but in different versions.That causes a potential conflict.The best way to avoid this is for package authors to useversion ranges rather than specific versions whenspecifying dependencies.If some_package declares the dependencies aboveand another_package declares a compatibleurl_launcher dependency like '5.4.6' or^5.5.0, pub resolves the issue automatically.Platform-specific dependencies onGradle modules and/or CocoaPodsare solved in a similar way.Even if some_package and another_packagedeclare incompatible versions for url_launcher,they might actually use url_launcher incompatible ways. In this situation,the conflict can be resolved by addinga dependency override declaration to the app’spubspec.yaml file, forcing the use of a particular version.For example, to force the use of url_launcher version 5.4.0,make the following changes to the app’s pubspec.yaml file:If the conflicting dependency is not itself a package,but an Android-specific library like guava,the dependency override declaration must be added toGradle build logic instead.To force the use of guava version 28.0, make the followingchanges to the app’s android/build.gradle file:CocoaPods doesn’t currently offer dependencyoverride functionality.<topic_end><topic_start>Developing new packagesIf no package exists for your specific use case,you can write a custom package.<topic_end><topic_start>Managing package dependencies and versionsTo minimize the risk of version collisions,specify a version range in the pubspec.yaml file.<topic_end><topic_start>Package versionsAll packages have a version number, specified in thepackage’s pubspec.yaml file. The current version of a packageis displayed next to its name (for example,see the url_launcher package), aswell as a list of all prior versions(see url_launcher versions).To ensure that the app doesn’t break when you update a package,specify a version range using one of the following formats.Ranged constraints: Specify a minimum and maximum version.Ranged constraints using the caret syntax:Specify the version that serves as the inclusive minimum version.This covers all versions from that version to the next major version.This syntax means the same as the one noted in the first bullet.To learn more, check out the package versioning guide.<topic_end><topic_start>Updating package dependenciesWhen running flutter pub getfor the first time after adding a package,Flutter saves the concrete package version found in the pubspec.locklockfile. This ensures that you get the same version againif you, or another developer on your team, run flutter pub get.To upgrade to a new version of the package,for example to use new features in that package,run flutter pub upgradeto retrieve the highest available version of the packagethat is allowed by the version constraint specified inpubspec.yaml.Note that this is a different command fromflutter upgrade or flutter update-packages,which both update Flutter itself.<topic_end><topic_start>Dependencies on unpublished packagesPackages can be used even when not published on pub.dev.For private packages, or for packages not ready for publishing,additional dependency options are available:Finally, use the ref argument to pin the dependency to aspecific git commit, branch, or tag. For more details, seePackage dependencies.<topic_end><topic_start>ExamplesThe following examples walk through the necessary steps forusing packages.<topic_end><topic_start>Example: Using the css_colors packageThe css_colors packagedefines color constants for CSS colors, so use the constantswherever the Flutter framework expects the Color type.To use this package:Create a new project called cssdemo.Open pubspec.yaml, and add the css-colors dependency:Run flutter pub get in the terminal,or click Get Packages in VS Code.Open lib/main.dart and replace its full contents with:<code_start>import 'package:css_colors/css_colors.dart'; import 'package:flutter/material.dart'; void main() {   runApp(const MyApp()); } class MyApp extends StatelessWidget {   const MyApp({super.key});   @override   Widget build(BuildContext context) {     return const MaterialApp(       home: DemoPage(),     );   } } class DemoPage extends StatelessWidget {   const DemoPage({super.key});   @override   Widget build(BuildContext context) {     return Scaffold(body: Container(color: CSSColors.orange));   } }<code_end><topic_end><topic_start>Example: Using the url_launcher package to launch the browserThe url_launcher plugin package enables openingthe default browser on the mobile platform to displaya given URL, and is supported on Android, iOS, web,Windows, Linux, and macOS.This package is a special Dart package called aplugin package (or plugin),which includes platform-specific code.To use this plugin:Create a new project called launchdemo.Open pubspec.yaml, and add the url_launcher dependency:Run flutter pub get in the terminal,or click Get Packages get in VS Code.Open lib/main.dart and replace its full contents with thefollowing:<code_start>import 'package:flutter/material.dart'; import 'package:path/path.dart' as p; import 'package:url_launcher/url_launcher.dart'; void main() {   runApp(const MyApp()); } class MyApp extends StatelessWidget {   const MyApp({super.key});   @override   Widget build(BuildContext context) {     return const MaterialApp(       home: DemoPage(),     );   } } class DemoPage extends StatelessWidget {   const DemoPage({super.key});   void launchURL() {     launchUrl(p.toUri('https://flutter.dev'));   }   @override   Widget build(BuildContext context) {     return Scaffold(       body: Center(         child: ElevatedButton(           onPressed: launchURL,           child: const Text('Show Flutter homepage'),         ),       ),     );   } }<code_end>Run the app (or stop and restart it, if it was already runningbefore adding the plugin). Click Show Flutter homepage.You should see the default browser open on the device,displaying the homepage for flutter.dev.<topic_end><topic_start>Developing packages & plugins<topic_end><topic_start>Package introductionPackages enable the creation of modular code that can be shared easily.A minimal package consists of the following:info Note  For a list of dos and don’ts when writing an effective plugin,  see the Medium article by Mehmet Fidanboylu,  Writing a good plugin.<topic_end><topic_start>Package typesPackages can contain more than one kind of content:Plugin packages can be written for Android(using Kotlin or Java), iOS (using Swift or Objective-C),web, macOS, Windows, or Linux, or any combinationthereof.A concrete example is the url_launcher plugin package.To see how to use the url_launcher package, and how itwas extended to implement support for web,see the Medium article by Harry Terkelsen,How to Write a Flutter Web Plugin, Part 1.<topic_end><topic_start>Developing Dart packagesThe following instructions explain how to write a Flutterpackage.<topic_end><topic_start>Step 1: Create the packageTo create a starter Flutter package,use the --template=package flag with flutter create:This creates a package project in the hellofolder with the following content:<topic_end><topic_start>Step 2: Implement the packageFor pure Dart packages, simply add the functionalityinside the main lib/<package name>.dart file,or in several files in the lib directory.To test the package, add unit testsin a test directory.For additional details on how to organize thepackage contents,see the Dart library package documentation.<topic_end><topic_start>Developing plugin packagesIf you want to develop a package that calls intoplatform-specific APIs,you need to develop a plugin package.The API is connected to the platform-specificimplementation(s) using a platform channel.<topic_end><topic_start>Federated pluginsFederated plugins are a way of splitting support fordifferent platforms into separate packages.So, a federated plugin can use one package for iOS,another for Android, another for web,and yet another for a car (as an example of an IoT device).Among other benefits, this approach allows a domain expertto extend an existing plugin to work for the platform they know best.A federated plugin requires the following packages:<topic_end><topic_start>Endorsed federated pluginIdeally, when adding a platform implementation toa federated plugin, you will coordinate with the packageauthor to include your implementation.In this way, the original author endorses yourimplementation.For example, say you write a foobar_windowsimplementation for the (imaginary) foobar plugin.In an endorsed plugin, the original foobar authoradds your Windows implementation as a dependencyin the pubspec for the app-facing package.Then, when a developer includes the foobar pluginin their Flutter app, the Windows implementation,as well as the other endorsed implementations,are automatically available to the app.<topic_end><topic_start>Non-endorsed federated pluginIf you can’t, for whatever reason, get your implementationadded by the original plugin author, then your pluginis not endorsed. A developer can still use yourimplementation, but must manually add the pluginto the app’s pubspec file. So, the developermust include both the foobar dependency andthe foobar_windows dependency in order to achievefull functionality.For more information on federated plugins,why they are useful, and how they areimplemented, see the Medium article by Harry Terkelsen,How To Write a Flutter Web Plugin, Part 2.<topic_end><topic_start>Specifying a plugin’s supported platformsPlugins can specify the platforms they support byadding keys to the platforms map in thepubspec.yaml file. For example,the following pubspec file shows theflutter: map for the hello plugin,which supports only iOS and Android:When adding plugin implementations for more platforms,the platforms map should be updated accordingly.For example, here’s the map in the pubspec filefor the hello plugin,when updated to add support for macOS and web:<topic_end><topic_start>Federated platform packagesA platform package uses the same format,but includes an implements entry indicatingwhich app-facing package it implements. For example,a hello_windows plugin containing the Windowsimplementation for hellowould have the following flutter: map:<topic_end><topic_start>Endorsed implementationsAn app facing package can endorse a platform package by adding adependency on it, and including it as a default_package in theplatforms: map. If the hello plugin above endorsed hello_windows,it would look as follows:Note that as shown here, an app-facing package can havesome platforms implemented within the package,and others in endorsed federated implementations.<topic_end><topic_start>Shared iOS and macOS implementationsMany frameworks support both iOS and macOS with identicalor mostly identical APIs, making it possible to implementsome plugins for both iOS and macOS with the same codebase.Normally each platform’s implementation is in its ownfolder, but the sharedDarwinSource option allows iOSand macOS to use the same folder instead:When sharedDarwinSource is enabled, instead ofan ios directory for iOS and a macos directoryfor macOS, both platforms use a shared darwindirectory for all code and resources. When enablingthis option, you need to move any existing filesfrom ios and macos to the shared directory. Youalso need to update the podspec file to set thedependencies and deployment targets for both platforms,for example:<topic_end><topic_start>Step 1: Create the packageTo create a plugin package, use the --template=pluginflag with flutter create.Use the --platforms= option followed by acomma-separated list to specify the platformsthat the plugin supports. Available platforms are:android, ios, web, linux, macos, and windows.If no platforms are specified, theresulting project doesn’t support any platforms.Use the --org option to specify your organization,using reverse domain name notation. This value is usedin various package and bundle identifiers in thegenerated plugin code.Use the -a option to specify the language for androidor the -i option to specify the language for ios.Please choose one of the following:This creates a plugin project in the hello folderwith the following specialized content:By default, the plugin project uses Swift for iOS code andKotlin for Android code. If you prefer Objective-C or Java,you can specify the iOS language using -i and theAndroid language using -a. For example:<topic_end><topic_start>Step 2: Implement the packageAs a plugin package contains code for several platformswritten in several programming languages,some specific steps are needed to ensure a smooth experience.<topic_end><topic_start>Step 2a: Define the package API (.dart)The API of the plugin package is defined in Dart code.Open the main hello/ folder in your favorite Flutter editor.Locate the file lib/hello.dart.<topic_end><topic_start>Step 2b: Add Android platform code (.kt/.java)We recommend you edit the Android code using Android Studio.Then use the following steps:The Android platform code of your plugin is located inhello/java/com.example.hello/HelloPlugin.You can run the example app from Android Studio bypressing the run (▶) button.<topic_end><topic_start>Step 2c: Add iOS platform code (.swift/.h+.m)We recommend you edit the iOS code using Xcode.Before editing the iOS platform code in Xcode,first make sure that the code has been built at least once(in other words, run the example app from your IDE/editor,or in a terminal executecd hello/example; flutter build ios --no-codesign).Then use the following steps:The iOS platform code for your plugin is located inPods/Development Pods/hello/../../example/ios/.symlinks/plugins/hello/ios/Classesin the Project Navigator. (If you are using sharedDarwinSource,the path will end with hello/darwin/Classes instead.)You can run the example app by pressing the run (▶) button.<topic_end><topic_start>Add CocoaPod dependenciesUse the following instructions to add HelloPod with the version 0.0.1:Specify dependency at the end of ios/hello.podspec:For private pods, refer to Private CocoaPods to ensure repo access:Installing the pluginThe pod should appear in the installation summary.<topic_end><topic_start>Step 2d: Add Linux platform code (.h+.cc)We recommend you edit the Linux code using an IDE withC++ integration. The instructions below are forVisual Studio Code with the “C/C++” and “CMake” extensionsinstalled, but can be adjusted for other IDEs.Before editing the Linux platform code in an IDE,first make sure that the code has been built at least once(in other words, run the example app from your FlutterIDE/editor, or in a terminal executecd hello/example; flutter build linux).Then use the following steps:The Linux platform code for your plugin is located influtter/ephemeral/.plugin_symlinks/hello/linux/.You can run the example app using flutter run.Note: Creating a runnable Flutter applicationon Linux requires steps that are part of the fluttertool, so even if your editor provides CMakeintegration building and running that way won’twork correctly.<topic_end><topic_start>Step 2e: Add macOS platform code (.swift)We recommend you edit the macOS code using Xcode.Before editing the macOS platform code in Xcode,first make sure that the code has been built at least once(in other words, run the example app from your IDE/editor,or in a terminal executecd hello/example; flutter build macos).Then use the following steps:The macOS platform code for your plugin is located inPods/Development Pods/hello/../../example/macos/Flutter/ephemeral/.symlinks/plugins/hello/macos/Classesin the Project Navigator. (If you are using sharedDarwinSource,the path will end with hello/darwin/Classes instead.)You can run the example app by pressing the run (▶) button.<topic_end><topic_start>Step 2f: Add Windows platform code (.h+.cpp)We recommend you edit the Windows code using Visual Studio.Before editing the Windows platform code in Visual Studio,first make sure that the code has been built at least once(in other words, run the example app from your IDE/editor,or in a terminal executecd hello/example; flutter build windows).Then use the following steps:The Windows platform code for your plugin is located inhello_plugin/Source Files and hello_plugin/Header Files inthe Solution Explorer.You can run the example app by right-clicking hello_example inthe Solution Explorer and selecting Set as Startup Project,then pressing the run (▶) button. Important: Aftermaking changes to plugin code, you must selectBuild > Build Solution before running again, otherwisean outdated copy of the built plugin will be run insteadof the latest version containing your changes.<topic_end><topic_start>Step 2g: Connect the API and the platform codeFinally, you need to connect the API written in Dart code withthe platform-specific implementations.This is done using a platform channel,or through the interfaces defined in a platforminterface package.<topic_end><topic_start>Add support for platforms in an existing plugin projectTo add support for specific platforms to anexisting plugin project, run flutter create withthe --template=plugin flag again in the project directory.For example, to add web support in an existing plugin, run:If this command displays a message about updating thepubspec.yaml file, follow the provided instructions.<topic_end><topic_start>Dart platform implementationsIn many cases, non-web platform implementations only use theplatform-specific implementation language, as shown above. However,platform implementations can also use platform-specific Dart as well.info Note  The examples below only apply to non-web platforms. Web  plugin implementations are always written in Dart, and use  pluginClass and fileName for their Dart implementations  as shown above.<topic_end><topic_start>Dart-only platform implementationsIn some cases, some platforms can beimplemented entirely in Dart (for example, using FFI).For a Dart-only platform implementation on a platform other than web,replace the pluginClass in pubspec.yaml with a dartPluginClass.Here is the hello_windows example above modified for aDart-only implementation:In this version you would have no C++ Windows code, and would insteadsubclass the hello plugin’s Dart platform interface class with aHelloPluginWindows class that includes a staticregisterWith() method.  This method is called during startup,and can be used to register the Dart implementation:<topic_end><topic_start>Hybrid platform implementationsPlatform implementations can also use both Dart and a platform-specificlanguage. For example, a plugin could use a different platform channelfor each platform so that the channels can be customized per platform.A hybrid implementation uses both of the registration systemsdescribed above. Here is the hello_windows example above modified for ahybrid implementation:The Dart HelloPluginWindows class would use the registerWith()shown above for Dart-only implementations, while the C++ HelloPluginclass would be the same as in a C++-only implementation.<topic_end><topic_start>Testing your pluginWe encourage you test your plugin with automated teststo ensure that functionality doesn’t regressas you make changes to your code.To learn more about testing your plugins,check out Testing plugins.If you are writing tests for your Flutter appand plugins are causing crashes,check out Flutter in plugin tests.<topic_end><topic_start>Developing FFI plugin packagesIf you want to develop a package that calls into native APIs usingDart’s FFI, you need to develop an FFI plugin package.Both FFI plugin packages and (non-FFI) plugin packages supportbundling native code, but FFI plugin packages do not supportmethod channels and do include method channel registration code.If you want to implement a plugin that uses both method channelsand FFI, use a (non-FFI) plugin. You can chose per platform touse an FFI or (non-FFI) plugin.FFI plugin packages were introduced in Flutter 3.0, if you’retargeting older Flutter versions, you can use a (non-FFI) plugin.<topic_end><topic_start>Step 1: Create the packageTo create a starter FFI plugin package,use the --template=plugin_ffi flag with flutter create:This creates an FFI plugin project in the hellofolder with the following specialized content:lib: The Dart code that defines the API of the plugin,  and which calls into the native code using dart:ffi.src: The native source code, and a CMakeLists.txt  file for building that source code into a dynamic library.platform folders (android, ios, windows, etc.): The  build files for building and bundling the native code  library with the platform application.<topic_end><topic_start>Step 2: Building and bundling native codeThe pubspec.yaml specifies FFI plugins as follows:This configuration invokes the native buildfor the various target platforms and bundlesthe binaries in Flutter applications using these FFI plugins.This can be combined with dartPluginClass,such as when FFI is used for theimplementation of one platform in a federated plugin:A plugin can have both FFI and method channels:The native build systems that are invoked by FFI(and method channels) plugins are:<topic_end><topic_start>Step 3: Binding to native codeTo use the native code, bindings in Dart are needed.To avoid writing these by hand,they are generated from the header file(src/hello.h) by package:ffigen.Reference the ffigen docs for informationon how to install this package.Regenerate the bindings by running the following:<topic_end><topic_start>Step 4: Invoking native codeVery short-running native functions can be directlyinvoked from any isolate.For an example, see sum in lib/hello.dart.Longer-running functions should be invoked on ahelper isolate to avoid dropping frames inFlutter applications.For an example, see sumAsync in lib/hello.dart.<topic_end><topic_start>Adding documentationIt is recommended practice to add the following documentationto all packages:<topic_end><topic_start>API documentationWhen you publish a package,API documentation is automatically generated andpublished to pub.dev/documentation.For example, see the docs for device_info.If you wish to generate API documentation locally onyour development machine, use the following commands:Change directory to the location of your package:Tell the documentation tool where the    Flutter SDK is located (change the following commands to reflect    where you placed it):Run the dart doc tool    (included as part of the Flutter SDK), as follows:For tips on how to write API documentation, seeEffective Dart Documentation.<topic_end><topic_start>Adding licenses to the LICENSE fileIndividual licenses inside each LICENSE fileshould be separated by 80 hyphenson their own on a line.If a LICENSE file contains more than onecomponent license, then each componentlicense must start with the names of thepackages to which the component license applies,with each package name on its own line,and the list of package names separated fromthe actual license text by a blank line.(The packages need not match the names ofthe pub package. For example, a package might itself containcode from multiple third-party sources,and might need to include a license for each one.)The following example shows a well-organized license file:Here is another example of a well-organized license file:Here is an example of a poorly-organized license file:Another example of a poorly-organized license file:<topic_end><topic_start>Publishing your packagelightbulb Tip  Have you noticed that some of the packages and plugins  on pub.dev are designated as Flutter Favorites?  These are the packages published by verified developers  and are identified as the packages and plugins you  should first consider using when writing your app.  To learn more,  see the Flutter Favorites program.Once you have implemented a package, you can publish it onpub.dev, so that other developers can easily use it.Prior to publishing, make sure to review the pubspec.yaml,README.md, and CHANGELOG.md files to make sure theircontent is complete and correct. Also, to improve thequality and usability of your package (and to make itmore likely to achieve the status of a Flutter Favorite),consider including the following items:Next, run the publish command in dry-run modeto see if everything passes analysis:The next step is publishing to pub.dev,but be sure that you are ready becausepublishing is forever:For more details on publishing, see thepublishing docs on dart.dev.<topic_end><topic_start>Handling package interdependenciesIf you are developing a package hello that depends onthe Dart API exposed by another package, you need to addthat package to the dependencies section of yourpubspec.yaml file. The code below makes the Dart APIof the url_launcher plugin available to hello:You can now import 'package:url_launcher/url_launcher.dart'and launch(someUrl) in the Dart code of hello.This is no different from how you include packages inFlutter apps or any other Dart project.But if hello happens to be a plugin packagewhose platform-specific code needs accessto the platform-specific APIs exposed by url_launcher,you also need to add suitable dependency declarationsto your platform-specific build files, as shown below.<topic_end><topic_start>AndroidThe following example sets a dependency forurl_launcher in hello/android/build.gradle:You can now import io.flutter.plugins.urllauncher.UrlLauncherPluginand access the UrlLauncherPluginclass in the source code at hello/android/src.For more information on build.gradle files, see theGradle Documentation on build scripts.<topic_end><topic_start>iOSThe following example sets a dependency forurl_launcher in hello/ios/hello.podspec:You can now #import "UrlLauncherPlugin.h" andaccess the UrlLauncherPlugin class in the source codeat hello/ios/Classes.For additional details on .podspec files, see theCocoaPods Documentation on them.<topic_end><topic_start>WebAll web dependencies are handled by the pubspec.yamlfile like any other Dart package.<topic_end><topic_start>Flutter Favorite programThe aim of the Flutter Favorite program is to identifypackages and plugins that you should first consider whenbuilding your app.This is not a guarantee of quality or suitability to yourparticular situation—you should always perform yourown evaluation of packages and plugins for your project.You can see the complete list ofFlutter Favorite packages on pub.dev.info Note  If you came here looking for the Happy Paths recommendations,  we have discontinued that project in favor of Flutter Favorites.<topic_end><topic_start>MetricsFlutter Favorite packages have passed high quality standardsusing the following metrics:<topic_end><topic_start>Flutter Ecosystem  CommitteeThe Flutter Ecosystem Committee is comprised of Flutterteam members and Flutter community members spreadacross its ecosystem.One of their jobs is to decide when a packagehas met the quality bar to become a Flutter Favorite.The current committee members(ordered alphabetically by last name)are as follows:If you’d like to nominate a package or plugin as apotential future Flutter Favorite, or would liketo bring any other issues to the attention of the committee,send the committee an email.<topic_end><topic_start>Flutter Favorite usage guidelinesFlutter Favorite packages are labeled as such on pub.devby the Flutter team.If you own a package that has been designated as a Flutter Favorite,you must adhere to the following guidelines:<topic_end><topic_start>What’s nextYou should expect the list of Flutter Favorite packagesto grow and change as the ecosystem continues to thrive.The committee will continue working with package authorsto increase quality, as well as consider other areas of theecosystem that could benefit from the Flutter Favorite program,such as tools, consulting firms, and prolific Flutter contributors.As the Flutter ecosystem grows,we’ll be looking at expanding the set of metrics,which might include the following:<topic_end><topic_start>Flutter FavoritesYou can see the complete list ofFlutter Favorite packages on pub.dev.<topic_end><topic_start>Testing Flutter appsThe more features your app has, the harder it is to test manually.Automated tests help ensure that your app performs correctly beforeyou publish it, while retaining your feature and bug fix velocity.info Note  For hands-on practice of testing Flutter apps, see the  How to test a Flutter app codelab.Automated testing falls into a few categories:Generally speaking, a well-tested app has many unit and widget tests,tracked by code coverage, plus enough integration teststo cover all the important use cases. This advice is based onthe fact that there are trade-offs between different kinds of testing,seen below.<topic_end><topic_start>Unit testsA unit test tests a single function, method, or class.The goal of a unit test is to verify the correctness of aunit of logic under a variety of conditions.External dependencies of the unit under test are generallymocked out.Unit tests generally don’t read from or writeto disk, render to screen, or receive user actions fromoutside the process running the test.For more information regarding unit tests,you can view the following recipesor run flutter test --help in your terminal.info Note  If you’re writing unit tests for code that  uses plugins and you want to avoid crashes,  check out Plugins in Flutter tests.  If you want to test your Flutter plugin,  check out Testing plugins.<topic_end><topic_start>RecipesAn introduction to unit testingMock dependencies using Mockito<topic_end><topic_start>Widget testsA widget test (in other UI frameworks referred to as component test)tests a single widget. The goal of a widget test is to verify that thewidget’s UI looks and interacts as expected. Testing a widget involvesmultiple classes and requires a test environment that provides theappropriate widget lifecycle context.For example, the Widget being tested should be able to receive andrespond to user actions and events, perform layout, and instantiate childwidgets. A widget test is therefore more comprehensive than a unit test.However, like a unit test, a widget test’s environment is replaced withan implementation much simpler than a full-blown UI system.<topic_end><topic_start>RecipesAn introduction to widget testingFind widgetsHandle scrollingTap, drag, and enter text<topic_end><topic_start>Integration testsAn integration test tests a complete app or a large part of an app.The goal of an integration test is to verify that all the widgetsand services being tested work together as expected.Furthermore, you can use integrationtests to verify your app’s performance.Generally, an integration test runs on a real device or an OS emulator,such as iOS Simulator or Android Emulator.The app under test is typically isolatedfrom the test driver code to avoid skewing the results.For more information on how to write integration tests, see the integrationtesting page.<topic_end><topic_start>RecipesAn introduction to integration testingPerformance profiling<topic_end><topic_start>Continuous integration servicesContinuous integration (CI) services allow you to run yourtests automatically when pushing new code changes.This provides timely feedback on whether the codechanges work as expected and do not introduce bugs.For information on running tests on various continuousintegration services, see the following:<topic_end><topic_start>An introduction to unit testingHow can you ensure that your app continues to work as youadd more features or change existing functionality?By writing tests.Unit tests are handy for verifying the behavior of a single function,method, or class. The test package provides thecore framework for writing unit tests, and the flutter_testpackage provides additional utilities for testing widgets.This recipe demonstrates the core features provided by the test packageusing the following steps:For more information about the test package,see the test package documentation.<topic_end><topic_start>1. Add the test dependencyThe test package provides the core functionality for writing tests in Dart. This is the best approach whenwriting packages consumed by web, server, and Flutter apps.To add the test package as a dev dependency,run flutter pub add:<topic_end><topic_start>2. Create a test fileIn this example, create two files: counter.dart and counter_test.dart.The counter.dart file contains a class that you want to test andresides in the lib folder. The counter_test.dart file containsthe tests themselves and lives inside the test folder.In general, test files should reside inside a test folderlocated at the root of your Flutter application or package.Test files should always end with _test.dart,this is the convention used by the test runner when searching for tests.When you’re finished, the folder structure should look like this:<topic_end><topic_start>3. Create a class to testNext, you need a “unit” to test. Remember: “unit” is another name for afunction, method, or class. For this example, create a Counter classinside the lib/counter.dart file. It is responsible for incrementingand decrementing a value starting at 0.<code_start>class Counter {  int value = 0;  void increment() => value++;  void decrement() => value--;}<code_end>Note: For simplicity, this tutorial does not follow the “Test DrivenDevelopment” approach. If you’re more comfortable with that style ofdevelopment, you can always go that route.<topic_end><topic_start>4. Write a test for our classInside the counter_test.dart file, write the first unit test. Tests aredefined using the top-level test function, and you can check if the resultsare correct by using the top-level expect function.Both of these functions come from the test package.<code_start>// Import the test package and Counter classimport 'package:counter_app/counter.dart';import 'package:test/test.dart';void main() {  test('Counter value should be incremented', () {    final counter = Counter();    counter.increment();    expect(counter.value, 1);  });}<code_end><topic_end><topic_start>5. Combine multiple tests in a groupIf you want to run a series of related tests,use the flutter_test package group function to categorize the tests.Once put into a group, you can call flutter test on all tests inthat group with one command.<code_start>import 'package:counter_app/counter.dart';import 'package:test/test.dart';void main() {  group('Test start, increment, decrement', () {    test('value should start at 0', () {      expect(Counter().value, 0);    });    test('value should be incremented', () {      final counter = Counter();      counter.increment();      expect(counter.value, 1);    });    test('value should be decremented', () {      final counter = Counter();      counter.decrement();      expect(counter.value, -1);    });  });}<code_end><topic_end><topic_start>6. Run the testsNow that you have a Counter class with tests in place,you can run the tests.<topic_end><topic_start>Run tests using IntelliJ or VSCodeThe Flutter plugins for IntelliJ and VSCode support running tests.This is often the best option while writing tests because it provides thefastest feedback loop as well as the ability to set breakpoints.IntelliJVSCode<topic_end><topic_start>Run tests in a terminalTo run the all tests from the terminal,run the following command from the root of the project:To run all tests you put into one group,run the following command from the root of the project:This example uses the group created in section 5.To learn more about unit tests, you can execute this command:<topic_end><topic_start>Mock dependencies using MockitoSometimes, unit tests might depend on classes that fetch data from liveweb services or databases. This is inconvenient for a few reasons:Therefore, rather than relying on a live web service or database,you can “mock” these dependencies. Mocks allow emulating a liveweb service or database and return specific results dependingon the situation.Generally speaking, you can mock dependencies by creating an alternativeimplementation of a class. Write these alternative implementations byhand or make use of the Mockito package as a shortcut.This recipe demonstrates the basics of mocking with theMockito package using the following steps:For more information, see the Mockito package documentation.<topic_end><topic_start>1. Add the package dependenciesTo use the mockito package, add it to thepubspec.yaml file along with the flutter_test dependency in thedev_dependencies section.This example also uses the http package,so define that dependency in the dependencies section.mockito: 5.0.0 supports Dart’s null safety thanks to code generation.To run the required code generation, add the build_runner dependencyin the dev_dependencies section.To add the dependencies, run flutter pub add:<topic_end><topic_start>2. Create a function to testIn this example, unit test the fetchAlbum function from theFetch data from the internet recipe.To test this function, make two changes:The function should now look like this:<code_start>Future<Album> fetchAlbum(http.Client client) async {  final response = await client      .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));  if (response.statusCode == 200) {    // If the server did return a 200 OK response,    // then parse the JSON.    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);  } else {    // If the server did not return a 200 OK response,    // then throw an exception.    throw Exception('Failed to load album');  }}<code_end>In your app code, you can provide an http.Client to the fetchAlbum method directly with fetchAlbum(http.Client()). http.Client() creates a defaulthttp.Client.<topic_end><topic_start>3. Create a test file with a mock http.ClientNext, create a test file.Following the advice in the Introduction to unit testing recipe,create a file called fetch_album_test.dart in the root test folder.Add the annotation @GenerateMocks([http.Client]) to the mainfunction to generate a MockClient class with mockito.The generated MockClient class implements the http.Client class.This allows you to pass the MockClient to the fetchAlbum function,and return different http responses in each test.The generated mocks will be located in fetch_album_test.mocks.dart.Import this file to use them.<code_start>import 'package:http/http.dart' as http;import 'package:mocking/main.dart';import 'package:mockito/annotations.dart';// Generate a MockClient using the Mockito package.// Create new instances of this class in each test.@GenerateMocks([http.Client])void main() {}<code_end>Next, generate the mocks running the following command:<topic_end><topic_start>4. Write a test for each conditionThe fetchAlbum() function does one of two things:Therefore, you want to test these two conditions.Use the MockClient class to return an “Ok” responsefor the success test, and an error response for the unsuccessful test.Test these conditions using the when() function provided byMockito:<code_start>import 'package:flutter_test/flutter_test.dart';import 'package:http/http.dart' as http;import 'package:mocking/main.dart';import 'package:mockito/annotations.dart';import 'package:mockito/mockito.dart';import 'fetch_album_test.mocks.dart';// Generate a MockClient using the Mockito package.// Create new instances of this class in each test.@GenerateMocks([http.Client])void main() {  group('fetchAlbum', () {    test('returns an Album if the http call completes successfully', () async {      final client = MockClient();      // Use Mockito to return a successful response when it calls the      // provided http.Client.      when(client              .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1')))          .thenAnswer((_) async =>              http.Response('{"userId": 1, "id": 2, "title": "mock"}', 200));      expect(await fetchAlbum(client), isA<Album>());    });    test('throws an exception if the http call completes with an error', () {      final client = MockClient();      // Use Mockito to return an unsuccessful response when it calls the      // provided http.Client.      when(client              .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1')))          .thenAnswer((_) async => http.Response('Not Found', 404));      expect(fetchAlbum(client), throwsException);    });  });}<code_end><topic_end><topic_start>5. Run the testsNow that you have a fetchAlbum() function with tests in place,run the tests.You can also run tests inside your favorite editor by following theinstructions in the Introduction to unit testing recipe.<topic_end><topic_start>Complete example<topic_end><topic_start>lib/main.dart<code_start>import 'dart:async';import 'dart:convert';import 'package:flutter/material.dart';import 'package:http/http.dart' as http;Future<Album> fetchAlbum(http.Client client) async {  final response = await client      .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));  if (response.statusCode == 200) {    // If the server did return a 200 OK response,    // then parse the JSON.    return Album.fromJson(jsonDecode(response.body) as Map<String, dynamic>);  } else {    // If the server did not return a 200 OK response,    // then throw an exception.    throw Exception('Failed to load album');  }}class Album {  final int userId;  final int id;  final String title;  const Album({required this.userId, required this.id, required this.title});  factory Album.fromJson(Map<String, dynamic> json) {    return Album(      userId: json['userId'] as int,      id: json['id'] as int,      title: json['title'] as String,    );  }}void main() => runApp(const MyApp());class MyApp extends StatefulWidget {  const MyApp({super.key});  @override  State<MyApp> createState() => _MyAppState();}class _MyAppState extends State<MyApp> {  late final Future<Album> futureAlbum;  @override  void initState() {    super.initState();    futureAlbum = fetchAlbum(http.Client());  }  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Fetch Data Example',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: Scaffold(        appBar: AppBar(          title: const Text('Fetch Data Example'),        ),        body: Center(          child: FutureBuilder<Album>(            future: futureAlbum,            builder: (context, snapshot) {              if (snapshot.hasData) {                return Text(snapshot.data!.title);              } else if (snapshot.hasError) {                return Text('${snapshot.error}');              }              // By default, show a loading spinner.              return const CircularProgressIndicator();            },          ),        ),      ),    );  }}<code_end><topic_end><topic_start>test/fetch_album_test.dart<code_start>import 'package:flutter_test/flutter_test.dart';import 'package:http/http.dart' as http;import 'package:mocking/main.dart';import 'package:mockito/annotations.dart';import 'package:mockito/mockito.dart';import 'fetch_album_test.mocks.dart';// Generate a MockClient using the Mockito package.// Create new instances of this class in each test.@GenerateMocks([http.Client])void main() {  group('fetchAlbum', () {    test('returns an Album if the http call completes successfully', () async {      final client = MockClient();      // Use Mockito to return a successful response when it calls the      // provided http.Client.      when(client              .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1')))          .thenAnswer((_) async =>              http.Response('{"userId": 1, "id": 2, "title": "mock"}', 200));      expect(await fetchAlbum(client), isA<Album>());    });    test('throws an exception if the http call completes with an error', () {      final client = MockClient();      // Use Mockito to return an unsuccessful response when it calls the      // provided http.Client.      when(client              .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1')))          .thenAnswer((_) async => http.Response('Not Found', 404));      expect(fetchAlbum(client), throwsException);    });  });}<code_end><topic_end><topic_start>SummaryIn this example, you’ve learned how to use Mockito to test functions or classesthat depend on web services or databases. This is only a short introduction tothe Mockito library and the concept of mocking. For more information,see the documentation provided by the Mockito package.<topic_end><topic_start>An introduction to widget testingIn the introduction to unit testing recipe,you learned how to test Dart classes using the test package.To test widget classes, you need a few additional tools provided by theflutter_test package, which ships with the Flutter SDK.The flutter_test package provides the following tools fortesting widgets:If this sounds overwhelming, don’t worry. Learn how all of these pieces fittogether throughout this recipe, which uses the following steps:<topic_end><topic_start>1. Add the flutter_test dependencyBefore writing tests, include the flutter_testdependency in the dev_dependencies section of the pubspec.yaml file.If creating a new Flutter project with the command line tools ora code editor, this dependency should already be in place.<topic_end><topic_start>2. Create a widget to testNext, create a widget for testing. For this recipe,create a widget that displays a title and message.<code_start>class MyWidget extends StatelessWidget {  const MyWidget({    super.key,    required this.title,    required this.message,  });  final String title;  final String message;  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Flutter Demo',      home: Scaffold(        appBar: AppBar(          title: Text(title),        ),        body: Center(          child: Text(message),        ),      ),    );  }}<code_end><topic_end><topic_start>3. Create a testWidgets testWith a widget to test, begin by writing your first test.Use the testWidgets() function provided by theflutter_test package to define a test.The testWidgets function allows you to define awidget test and creates a WidgetTester to work with.This test verifies that MyWidget displays a given title and message.It is titled accordingly, and it will be populated in the next section.<code_start>void main() {  // Define a test. The TestWidgets function also provides a WidgetTester  // to work with. The WidgetTester allows you to build and interact  // with widgets in the test environment.  testWidgets('MyWidget has a title and message', (tester) async {    // Test code goes here.  });}<code_end><topic_end><topic_start>4. Build the widget using the WidgetTesterNext, build MyWidget inside the test environment by using thepumpWidget() method provided by WidgetTester.The pumpWidget method builds and renders the provided widget.Create a MyWidget instance that displays “T” as the titleand “M” as the message.<code_start>void main() {  testWidgets('MyWidget has a title and message', (tester) async {    // Create the widget by telling the tester to build it.    await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));  });}<code_end><topic_end><topic_start>Notes about the pump() methodsAfter the initial call to pumpWidget(), the WidgetTester providesadditional ways to rebuild the same widget. This is useful if you’reworking with a StatefulWidget or animations.For example, tapping a button calls setState(), but Flutter won’tautomatically rebuild your widget in the test environment.Use one of the following methods to ask Flutter to rebuild the widget.info Note  To kick off the animation, you need to call pump()  once (with no duration specified) to start the ticker.  Without it, the animation does not start.These methods provide fine-grained control over the build lifecycle,which is particularly useful while testing.<topic_end><topic_start>5. Search for our widget using a FinderWith a widget in the test environment, searchthrough the widget tree for the title and messageText widgets using a Finder. This allows verification thatthe widgets are being displayed correctly.For this purpose, use the top-level find()method provided by the flutter_test package to create the Finders.Since you know you’re looking for Text widgets, use thefind.text() method.For more information about Finder classes, see theFinding widgets in a widget test recipe.<code_start>void main() {  testWidgets('MyWidget has a title and message', (tester) async {    await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));    // Create the Finders.    final titleFinder = find.text('T');    final messageFinder = find.text('M');  });}<code_end><topic_end><topic_start>6. Verify the widget using a MatcherFinally, verify the title and message Text widgets appear on screenusing the Matcher constants provided by flutter_test.Matcher classes are a core part of the test package,and provide a common way to verify a givenvalue meets expectations.Ensure that the widgets appear on screen exactly one time.For this purpose, use the findsOneWidget Matcher.<code_start>void main() {  testWidgets('MyWidget has a title and message', (tester) async {    await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));    final titleFinder = find.text('T');    final messageFinder = find.text('M');    // Use the `findsOneWidget` matcher provided by flutter_test to verify    // that the Text widgets appear exactly once in the widget tree.    expect(titleFinder, findsOneWidget);    expect(messageFinder, findsOneWidget);  });}<code_end><topic_end><topic_start>Additional MatchersIn addition to findsOneWidget, flutter_test provides additionalmatchers for common cases.<topic_end><topic_start>Complete example<code_start>import 'package:flutter/material.dart';import 'package:flutter_test/flutter_test.dart';void main() {  // Define a test. The TestWidgets function also provides a WidgetTester  // to work with. The WidgetTester allows building and interacting  // with widgets in the test environment.  testWidgets('MyWidget has a title and message', (tester) async {    // Create the widget by telling the tester to build it.    await tester.pumpWidget(const MyWidget(title: 'T', message: 'M'));    // Create the Finders.    final titleFinder = find.text('T');    final messageFinder = find.text('M');    // Use the `findsOneWidget` matcher provided by flutter_test to    // verify that the Text widgets appear exactly once in the widget tree.    expect(titleFinder, findsOneWidget);    expect(messageFinder, findsOneWidget);  });}class MyWidget extends StatelessWidget {  const MyWidget({    super.key,    required this.title,    required this.message,  });  final String title;  final String message;  @override  Widget build(BuildContext context) {    return MaterialApp(      title: 'Flutter Demo',      home: Scaffold(        appBar: AppBar(          title: Text(title),        ),        body: Center(          child: Text(message),        ),      ),    );  }}<code_end><topic_end><topic_start>Find widgetsTo locate widgets in a test environment, use the Finderclasses. While it’s possible to write your own Finder classes,it’s generally more convenient to locate widgets using the toolsprovided by the flutter_test package.During a flutter run session on a widget test, you can alsointeractively tap parts of the screen for the Flutter tool toprint the suggested Finder.This recipe looks at the find constant provided bythe flutter_test package, and demonstrates howto work with some of the Finders it provides.For a full list of available finders,see the CommonFinders documentation.If you’re unfamiliar with widget testing and the role ofFinder classes,review the Introduction to widget testing recipe.This recipe uses the following steps:<topic_end><topic_start>1. Find a Text widgetIn testing, you often need to find widgets that contain specific text.This is exactly what the find.text() method is for. It creates aFinder that searches for widgets that display a specific String of text.<code_start>testWidgets('finds a Text widget', (tester) async {  // Build an App with a Text widget that displays the letter 'H'.  await tester.pumpWidget(const MaterialApp(    home: Scaffold(      body: Text('H'),    ),  ));  // Find a widget that displays the letter 'H'.  expect(find.text('H'), findsOneWidget);});<code_end><topic_end><topic_start>2. Find a widget with a specific KeyIn some cases, you might want to find a widget based on the Key that has beenprovided to it. This can be handy if displaying multiple instances of thesame widget. For example, a ListView might display severalText widgets that contain the same text.In this case, provide a Key to each widget in the list. This allowsan app to uniquely identify a specific widget, making it easier to findthe widget in the test environment.<code_start>testWidgets('finds a widget using a Key', (tester) async {  // Define the test key.  const testKey = Key('K');  // Build a MaterialApp with the testKey.  await tester.pumpWidget(MaterialApp(key: testKey, home: Container()));  // Find the MaterialApp widget using the testKey.  expect(find.byKey(testKey), findsOneWidget);});<code_end><topic_end><topic_start>3. Find a specific widget instanceFinally, you might be interested in locating a specific instance of a widget.For example, this can be useful when creating widgets that take a childproperty and you want to ensure you’re rendering the child widget.<code_start>testWidgets('finds a specific instance', (tester) async {  const childWidget = Padding(padding: EdgeInsets.zero);  // Provide the childWidget to the Container.  await tester.pumpWidget(Container(child: childWidget));  // Search for the childWidget in the tree and verify it exists.  expect(find.byWidget(childWidget), findsOneWidget);});<code_end><topic_end><topic_start>SummaryThe find constant provided by the flutter_test package providesseveral ways to locate widgets in the test environment. This recipedemonstrated three of these methods, and several more methods existfor different purposes.If the above examples do not work for a particular use-case,see the CommonFinders documentationto review all available methods.<topic_end><topic_start>Complete example<code_start>import 'package:flutter/material.dart';import 'package:flutter_test/flutter_test.dart';void main() {  testWidgets('finds a Text widget', (tester) async {    // Build an App with a Text widget that displays the letter 'H'.    await tester.pumpWidget(const MaterialApp(      home: Scaffold(        body: Text('H'),      ),    ));    // Find a widget that displays the letter 'H'.    expect(find.text('H'), findsOneWidget);  });  testWidgets('finds a widget using a Key', (tester) async {    // Define the test key.    const testKey = Key('K');    // Build a MaterialApp with the testKey.    await tester.pumpWidget(MaterialApp(key: testKey, home: Container()));    // Find the MaterialApp widget using the testKey.    expect(find.byKey(testKey), findsOneWidget);  });  testWidgets('finds a specific instance', (tester) async {    const childWidget = Padding(padding: EdgeInsets.zero);    // Provide the childWidget to the Container.    await tester.pumpWidget(Container(child: childWidget));    // Search for the childWidget in the tree and verify it exists.    expect(find.byWidget(childWidget), findsOneWidget);  });}<code_end><topic_end><topic_start>Handle scrollingMany apps feature lists of content,from email clients to music apps and beyond.To verify that lists contain the expected contentusing widget tests,you need a way to scroll through lists to search for particular items.To scroll through lists via integration tests,use the methods provided by the WidgetTester class,which is included in the flutter_test package:In this recipe, learn how to scroll through a list of items toverify a specific widget is being displayed,and the pros and cons of different approaches.This recipe uses the following steps:<topic_end><topic_start>1. Create an app with a list of itemsThis recipe builds an app that shows a long list of items.To keep this recipe focused on testing, use the app created in theWork with long lists recipe.If you’re unsure of how to work with long lists,see that recipe for an introduction.Add keys to the widgets you want to interact withinside the integration tests.<code_start>import 'package:flutter/material.dart';void main() {  runApp(MyApp(    items: List<String>.generate(10000, (i) => 'Item $i'),  ));}class MyApp extends StatelessWidget {  final List<String> items;  const MyApp({super.key, required this.items});  @override  Widget build(BuildContext context) {    const title = 'Long List';    return MaterialApp(      title: title,      home: Scaffold(        appBar: AppBar(          title: const Text(title),        ),        body: ListView.builder(          // Add a key to the ListView. This makes it possible to          // find the list and scroll through it in the tests.          key: const Key('long_list'),          itemCount: items.length,          itemBuilder: (context, index) {            return ListTile(              title: Text(                items[index],                // Add a key to the Text widget for each item. This makes                // it possible to look for a particular item in the list                // and verify that the text is correct                key: Key('item_${index}_text'),              ),            );          },        ),      ),    );  }}<code_end><topic_end><topic_start>2. Write a test that scrolls through the listNow, you can write a test. In this example, scroll through the list of items andverify that a particular item exists in the list. The WidgetTester classprovides the scrollUntilVisible() method, which scrolls through a listuntil a specific widget is visible. This is useful because the height of theitems in the list can change depending on the device.Rather than assuming that you know the height of all the itemsin a list, or that a particular widget is rendered on all devices,the scrollUntilVisible() method repeatedly scrolls througha list of items until it finds what it’s looking for.The following code shows how to use the scrollUntilVisible() methodto look through the list for a particular item. This code lives in afile called test/widget_test.dart.<code_start>// This is a basic Flutter widget test.//// To perform an interaction with a widget in your test, use the WidgetTester// utility that Flutter provides. For example, you can send tap and scroll// gestures. You can also use WidgetTester to find child widgets in the widget// tree, read text, and verify that the values of widget properties are correct.import 'package:flutter/material.dart';import 'package:flutter_test/flutter_test.dart';import 'package:scrolling/main.dart';void main() {  testWidgets('finds a deep item in a long list', (tester) async {    // Build our app and trigger a frame.    await tester.pumpWidget(MyApp(      items: List<String>.generate(10000, (i) => 'Item $i'),    ));    final listFinder = find.byType(Scrollable);    final itemFinder = find.byKey(const ValueKey('item_50_text'));    // Scroll until the item to be found appears.    await tester.scrollUntilVisible(      itemFinder,      500.0,      scrollable: listFinder,    );    // Verify that the item contains the correct text.    expect(itemFinder, findsOneWidget);  });}<code_end><topic_end><topic_start>3. Run the testRun the test using the following command from the root of the project:<topic_end><topic_start>Tap, drag, and enter textMany widgets not only display information, but also respondto user interaction. This includes buttons that can be tapped,and TextField for entering text.To test these interactions, you need a way to simulate themin the test environment. For this purpose, use theWidgetTester library.The WidgetTester provides methods for entering text,tapping, and dragging.In many cases, user interactions update the state of the app. In the testenvironment, Flutter doesn’t automatically rebuild widgets when the statechanges. To ensure that the widget tree is rebuilt after simulating a userinteraction, call the pump() or pumpAndSettle()methods provided by the WidgetTester.This recipe uses the following steps:<topic_end><topic_start>1. Create a widget to testFor this example,create a basic todo app that tests three features:To keep the focus on testing,this recipe won’t provide a detailed guide on how to build the todo app.To learn more about how this app is built,see the relevant recipes:<code_start>class TodoList extends StatefulWidget {  const TodoList({super.key});  @override  State<TodoList> createState() => _TodoListState();}class _TodoListState extends State<TodoList> {  static const _appTitle = 'Todo List';  final todos = <String>[];  final controller = TextEditingController();  @override  Widget build(BuildContext context) {    return MaterialApp(      title: _appTitle,      home: Scaffold(        appBar: AppBar(          title: const Text(_appTitle),        ),        body: Column(          children: [            TextField(              controller: controller,            ),            Expanded(              child: ListView.builder(                itemCount: todos.length,                itemBuilder: (context, index) {                  final todo = todos[index];                  return Dismissible(                    key: Key('$todo$index'),                    onDismissed: (direction) => todos.removeAt(index),                    background: Container(color: Colors.red),                    child: ListTile(title: Text(todo)),                  );                },              ),            ),          ],        ),        floatingActionButton: FloatingActionButton(          onPressed: () {            setState(() {              todos.add(controller.text);              controller.clear();            });          },          child: const Icon(Icons.add),        ),      ),    );  }}<code_end><topic_end><topic_start>2. Enter text in the text fieldNow that you have a todo app, begin writing the test.Start by entering text into the TextField.Accomplish this task by:<code_start>testWidgets('Add and remove a todo', (tester) async {  // Build the widget  await tester.pumpWidget(const TodoList());  // Enter 'hi' into the TextField.  await tester.enterText(find.byType(TextField), 'hi');});<code_end>info Note  This recipe builds upon previous widget testing recipes.  To learn the core concepts of widget testing,  see the following recipes:<topic_end><topic_start>3. Ensure tapping a button adds the todoAfter entering text into the TextField, ensure that tappingthe FloatingActionButton adds the item to the list.This involves three steps:<code_start>testWidgets('Add and remove a todo', (tester) async {  // Enter text code...  // Tap the add button.  await tester.tap(find.byType(FloatingActionButton));  // Rebuild the widget after the state has changed.  await tester.pump();  // Expect to find the item on screen.  expect(find.text('hi'), findsOneWidget);});<code_end><topic_end><topic_start>4. Ensure swipe-to-dismiss removes the todoFinally, ensure that performing a swipe-to-dismiss action on the todoitem removes it from the list. This involves three steps:<code_start>testWidgets('Add and remove a todo', (tester) async {  // Enter text and add the item...  // Swipe the item to dismiss it.  await tester.drag(find.byType(Dismissible), const Offset(500, 0));  // Build the widget until the dismiss animation ends.  await tester.pumpAndSettle();  // Ensure that the item is no longer on screen.  expect(find.text('hi'), findsNothing);});<code_end><topic_end><topic_start>Complete example<code_start>import 'package:flutter/material.dart';import 'package:flutter_test/flutter_test.dart';void main() {  testWidgets('Add and remove a todo', (tester) async {    // Build the widget.    await tester.pumpWidget(const TodoList());    // Enter 'hi' into the TextField.    await tester.enterText(find.byType(TextField), 'hi');    // Tap the add button.    await tester.tap(find.byType(FloatingActionButton));    // Rebuild the widget with the new item.    await tester.pump();    // Expect to find the item on screen.    expect(find.text('hi'), findsOneWidget);    // Swipe the item to dismiss it.    await tester.drag(find.byType(Dismissible), const Offset(500, 0));    // Build the widget until the dismiss animation ends.    await tester.pumpAndSettle();    // Ensure that the item is no longer on screen.    expect(find.text('hi'), findsNothing);  });}class TodoList extends StatefulWidget {  const TodoList({super.key});  @override  State<TodoList> createState() => _TodoListState();}class _TodoListState extends State<TodoList> {  static const _appTitle = 'Todo List';  final todos = <String>[];  final controller = TextEditingController();  @override  Widget build(BuildContext context) {    return MaterialApp(      title: _appTitle,      home: Scaffold(        appBar: AppBar(          title: const Text(_appTitle),        ),        body: Column(          children: [            TextField(              controller: controller,            ),            Expanded(              child: ListView.builder(                itemCount: todos.length,                itemBuilder: (context, index) {                  final todo = todos[index];                  return Dismissible(                    key: Key('$todo$index'),                    onDismissed: (direction) => todos.removeAt(index),                    background: Container(color: Colors.red),                    child: ListTile(title: Text(todo)),                  );                },              ),            ),          ],        ),        floatingActionButton: FloatingActionButton(          onPressed: () {            setState(() {              todos.add(controller.text);              controller.clear();            });          },          child: const Icon(Icons.add),        ),      ),    );  }}<code_end><topic_end><topic_start>An introduction to integration testingUnit tests and widget tests are handy for testing individual classes,functions, or widgets. However, they generally don’t test howindividual pieces work together as a whole, or capture the performanceof an application running on a real device. These tasks are performedwith integration tests.Integration tests are written using the integration_test package, providedby the SDK.In this recipe, learn how to test a counter app. It demonstrateshow to set up integration tests, how to verify specific text is displayedby the app, how to tap specific widgets, and how to run integration tests.This recipe uses the following steps:<topic_end><topic_start>1. Create an app to testFirst, create an app for testing. In this example,test the counter app produced by the flutter createcommand. This app allows a user to tap on a buttonto increase a counter.<code_start>import 'package:flutter/material.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return const MaterialApp(      title: 'Counter App',      home: MyHomePage(title: 'Counter App Home Page'),    );  }}class MyHomePage extends StatefulWidget {  const MyHomePage({super.key, required this.title});  final String title;  @override  State<MyHomePage> createState() => _MyHomePageState();}class _MyHomePageState extends State<MyHomePage> {  int _counter = 0;  void _incrementCounter() {    setState(() {      _counter++;    });  }  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: Text(widget.title),      ),      body: Center(        child: Column(          mainAxisAlignment: MainAxisAlignment.center,          children: <Widget>[            const Text(              'You have pushed the button this many times:',            ),            Text(              '$_counter',              style: Theme.of(context).textTheme.headlineMedium,            ),          ],        ),      ),      floatingActionButton: FloatingActionButton(        // Provide a Key to this button. This allows finding this        // specific button inside the test suite, and tapping it.        key: const Key('increment'),        onPressed: _incrementCounter,        tooltip: 'Increment',        child: const Icon(Icons.add),      ),    );  }}<code_end><topic_end><topic_start>2. Add the integration_test dependencyNext, use the integration_test and flutter_test packagesto write integration tests. Add these dependencies to the dev_dependenciessection of the app’s pubspec.yaml file.<topic_end><topic_start>3. Create the test filesCreate a new directory, integration_test, with an empty app_test.dart file:<topic_end><topic_start>4. Write the integration testNow you can write tests. This involves three steps:<code_start>import 'package:flutter/material.dart';import 'package:flutter_test/flutter_test.dart';import 'package:integration_test/integration_test.dart';import 'package:introduction/main.dart';void main() {  IntegrationTestWidgetsFlutterBinding.ensureInitialized();  group('end-to-end test', () {    testWidgets('tap on the floating action button, verify counter',        (tester) async {      // Load app widget.      await tester.pumpWidget(const MyApp());      // Verify the counter starts at 0.      expect(find.text('0'), findsOneWidget);      // Finds the floating action button to tap on.      final fab = find.byKey(const Key('increment'));      // Emulate a tap on the floating action button.      await tester.tap(fab);      // Trigger a frame.      await tester.pumpAndSettle();      // Verify the counter increments by 1.      expect(find.text('1'), findsOneWidget);    });  });}<code_end><topic_end><topic_start>5. Run the integration testThe process of running the integration tests varies depending on the platformyou are testing against. You can test against a mobile platform or the web.<topic_end><topic_start>5a. MobileTo test on a real iOS / Android device, first connect the device and run thefollowing command from the root of the project:Or, you can specify the directory to run all integration tests:This command runs the app and integration tests on the target device. For moreinformation, see the Integration testing page.<topic_end><topic_start>5b. WebTo get started testing in a web browser, Download ChromeDriver.Next, create a new directory named test_driver containing a new filenamed integration_test.dart:<code_start>import 'package:integration_test/integration_test_driver.dart';Future<void> main() => integrationDriver();<code_end>Launch chromedriver as follows:From the root of the project, run the following command:For a headless testing experience, you can also run flutter drive with web-server as the target device identifier as follows:<topic_end><topic_start>Integration testingThis page describes how to use the integration_test package to runintegration tests. Tests written using this package have the followingproperties:info Note  The integration_test package is part of the Flutter SDK itself.  To use it, make sure that you update your app’s pubspec file  to include this package as one of your dev_dependencies.  For an example, see the Project setup section below.<topic_end><topic_start>OverviewUnit tests, widget tests, and integration testsThere are three types of tests that Flutter supports.A unit test verifies the behavior of a method or class.A widget test verifies the behavior of Flutter widgetswithout running the app itself. An integration test (alsocalled end-to-end testing or GUI testing) runs the full app.Hosts and targetsDuring development, you are probably writing the codeon a desktop computer, called the host machine,and running the app on a mobile device, browser,or desktop application, called the target device.(If you are using a webbrowser or desktop application,the host machine is also the target device.)integration_testTests written with the integration_test package can:Migrating from flutter_driverExisting projects using flutter_driver can be migrated tointegration_test by following the Migrating from flutter_driveguide.<topic_end><topic_start>Project setupAdd integration_test and flutter_test to your pubspec.yaml file:In your project, create a new directoryintegration_test with a new file, <name>_test.dart:<code_start>import 'package:flutter/material.dart';import 'package:flutter_test/flutter_test.dart';import 'package:how_to/main.dart';import 'package:integration_test/integration_test.dart';void main() {  testWidgets('tap on the floating action button, verify counter',      (tester) async {    // Load app widget.    await tester.pumpWidget(const MyApp());    // Verify the counter starts at 0.    expect(find.text('0'), findsOneWidget);    // Finds the floating action button to tap on.    final fab = find.byKey(const Key('increment'));    // Emulate a tap on the floating action button.    await tester.tap(fab);    // Trigger a frame.    await tester.pumpAndSettle();    // Verify the counter increments by 1.    expect(find.text('0'), findsNothing);    expect(find.text('1'), findsOneWidget);  });}<code_end>info Note  Only use testWidgets to declare your tests.  Otherwise, Flutter could report errors incorrectly.If you are looking for more examples, take a look at the testing_app ofthe samples repository.<topic_end><topic_start>Directory structureSee also:<topic_end><topic_start>Running using the flutter commandThese tests can be launched with theflutter test command, where <DEVICE_ID>:is the optional device ID or pattern displayedin the output of the flutter devices command:This runs the tests in foo_test.dart. To run all tests in this directory onthe default device, run:<topic_end><topic_start>Running in a browserDownload and install ChromeDriverand run it on port 4444:To run tests with flutter drive, create a new directory containing a new file,test_driver/integration_test.dart:<code_start>import 'package:integration_test/integration_test_driver.dart';Future<void> main() => integrationDriver();<code_end>Then add IntegrationTestWidgetsFlutterBinding.ensureInitialized() in yourintegration_test/<name>_test.dart file:<code_start>import 'package:flutter/material.dart';import 'package:flutter_test/flutter_test.dart';import 'package:how_to/main.dart';import 'package:integration_test/integration_test.dart';void main() {  IntegrationTestWidgetsFlutterBinding.ensureInitialized(); // NEW  testWidgets('tap on the floating action button, verify counter',      (tester) async {    // Load app widget.    await tester.pumpWidget(const MyApp());    // Verify the counter starts at 0.    expect(find.text('0'), findsOneWidget);    // Finds the floating action button to tap on.    final fab = find.byKey(const Key('increment'));    // Emulate a tap on the floating action button.    await tester.tap(fab);    // Trigger a frame.    await tester.pumpAndSettle();    // Verify the counter increments by 1.    expect(find.text('0'), findsNothing);    expect(find.text('1'), findsOneWidget);  });}<code_end>In a separate process, run flutter_drive:To learn more, see theRunning Flutter driver tests with web wiki page.<topic_end><topic_start>Testing on Firebase Test LabYou can use the Firebase Test Lab with both Androidand iOS targets.<topic_end><topic_start>Android setupFollow the instructions in the Android Device Testingsection of the README.<topic_end><topic_start>iOS setupFollow the instructions in the iOS Device Testingsection of the README.<topic_end><topic_start>Test Lab project setupGo to the Firebase Console,and create a new project if you don’t have onealready. Then navigate to Quality > Test Lab:<topic_end><topic_start>Uploading an Android APKCreate an APK using Gradle:Where <name>_test.dart is the file created in theProject Setup section.info Note  To use --dart-define with gradlew, you must base64 encode  all parameters, and pass them to gradle in a comma separated list:Drag the “debug” APK from<flutter_project_directory>/build/app/outputs/apk/debuginto the Android Robo Test target on the web page.This starts a Robo test and allows you to runother tests:Click Run a test,select the Instrumentation test type and dragthe following two files:If a failure occurs,you can view the output by selecting the red icon:<topic_end><topic_start>Uploading an Android APK from the command lineSee the Firebase Test Lab section of the READMEfor instructions on uploading the APKs from the command line.<topic_end><topic_start>Uploading Xcode testsSee the Firebase TestLab iOS instructionsfor details on how to upload the .zip fileto the Firebase TestLab section of the Firebase Console.<topic_end><topic_start>Uploading Xcode tests from the command lineSee the iOS Device Testing section in the READMEfor instructions on how to upload the .zip filefrom the command line.<topic_end><topic_start>Performance profilingWhen it comes to mobile apps, performance is critical to user experience.Users expect apps to have smooth scrolling and meaningful animations free ofstuttering or skipped frames, known as “jank.” How to ensure that your appis free of jank on a wide variety of devices?There are two options: first, manually test the app on different devices.While that approach might work for a smaller app, it becomes morecumbersome as an app grows in size. Alternatively, run an integrationtest that performs a specific task and records a performance timeline.Then, examine the results to determine whether a specific section ofthe app needs to be improved.In this recipe, learn how to write a test that records a performancetimeline while performing a specific task and saves a summary of theresults to a local file.info Note  Recording performance timelines isn’t supported on web.  For performance profiling on web, see  Debugging performance for web appsThis recipe uses the following steps:<topic_end><topic_start>1. Write a test that scrolls through a list of itemsIn this recipe, record the performance of an app as it scrolls through alist of items. To focus on performance profiling, this recipe buildson the Scrolling recipe in widget tests.Follow the instructions in that recipe to create an app and write a test toverify that everything works as expected.<topic_end><topic_start>2. Record the performance of the appNext, record the performance of the app as it scrolls through thelist. Perform this task using the traceAction()method provided by the IntegrationTestWidgetsFlutterBinding class.This method runs the provided function and records a Timelinewith detailed information about the performance of the app. This exampleprovides a function that scrolls through the list of items,ensuring that a specific item is displayed. When the function completes,the traceAction() creates a report data Map that contains the Timeline.Specify the reportKey when running more than one traceAction.By default all Timelines are stored with the key timeline,in this example the reportKey is changed to scrolling_timeline.<code_start>await binding.traceAction(  () async {    // Scroll until the item to be found appears.    await tester.scrollUntilVisible(      itemFinder,      500.0,      scrollable: listFinder,    );  },  reportKey: 'scrolling_timeline',);<code_end><topic_end><topic_start>3. Save the results to diskNow that you’ve captured a performance timeline, you need a way to review it.The Timeline object provides detailed information about all of the eventsthat took place, but it doesn’t provide a convenient way to review the results.Therefore, convert the Timeline into a TimelineSummary.The TimelineSummary can perform two tasks that make it easierto review the results:To capture the results, create a file named perf_driver.dartin the test_driver folder and add the following code:<code_start>import 'package:flutter_driver/flutter_driver.dart' as driver;import 'package:integration_test/integration_test_driver.dart';Future<void> main() {  return integrationDriver(    responseDataCallback: (data) async {      if (data != null) {        final timeline = driver.Timeline.fromJson(          data['scrolling_timeline'] as Map<String, dynamic>,        );        // Convert the Timeline into a TimelineSummary that's easier to        // read and understand.        final summary = driver.TimelineSummary.summarize(timeline);        // Then, write the entire timeline to disk in a json format.        // This file can be opened in the Chrome browser's tracing tools        // found by navigating to chrome://tracing.        // Optionally, save the summary to disk by setting includeSummary        // to true        await summary.writeTimelineToFile(          'scrolling_timeline',          pretty: true,          includeSummary: true,        );      }    },  );}<code_end>The integrationDriver function has a responseDataCallback which you can customize. By default, it writes the results to the integration_response_data.json file,but you can customize it to generate a summary like in this example.<topic_end><topic_start>4. Run the testAfter configuring the test to capture a performance Timeline and save asummary of the results to disk, run the test with the following command:The --profile option means to compile the app for the “profile mode” rather than the “debug mode”, so that the benchmark result is closer to what will be experienced by end users.info Note  Run the command with --no-dds when running on a mobile device or emulator.  This option disables the Dart Development Service (DDS), which won’t  be accessible from your computer.<topic_end><topic_start>5. Review the resultsAfter the test completes successfully, the build directory at the root ofthe project contains two files:<topic_end><topic_start>Summary example<topic_end><topic_start>Complete exampleintegration_test/scrolling_test.dart<code_start>import 'package:flutter/material.dart';import 'package:flutter_test/flutter_test.dart';import 'package:integration_test/integration_test.dart';import 'package:scrolling/main.dart';void main() {  final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();  testWidgets('Counter increments smoke test', (tester) async {    // Build our app and trigger a frame.    await tester.pumpWidget(MyApp(      items: List<String>.generate(10000, (i) => 'Item $i'),    ));    final listFinder = find.byType(Scrollable);    final itemFinder = find.byKey(const ValueKey('item_50_text'));    await binding.traceAction(      () async {        // Scroll until the item to be found appears.        await tester.scrollUntilVisible(          itemFinder,          500.0,          scrollable: listFinder,        );      },      reportKey: 'scrolling_timeline',    );  });}<code_end>test_driver/perf_driver.dart<code_start>import 'package:flutter_driver/flutter_driver.dart' as driver;import 'package:integration_test/integration_test_driver.dart';Future<void> main() {  return integrationDriver(    responseDataCallback: (data) async {      if (data != null) {        final timeline = driver.Timeline.fromJson(          data['scrolling_timeline'] as Map<String, dynamic>,        );        // Convert the Timeline into a TimelineSummary that's easier to        // read and understand.        final summary = driver.TimelineSummary.summarize(timeline);        // Then, write the entire timeline to disk in a json format.        // This file can be opened in the Chrome browser's tracing tools        // found by navigating to chrome://tracing.        // Optionally, save the summary to disk by setting includeSummary        // to true        await summary.writeTimelineToFile(          'scrolling_timeline',          pretty: true,          includeSummary: true,        );      }    },  );}<code_end><topic_end><topic_start>Testing pluginsAll of the usual types of Flutter tests apply toplugin packages as well, but because plugins containnative code they often also require other kinds of teststo test all of their functionality.info Note  To learn how to test your plugin code, read on.  To learn how to avoid crashes from a plugin when  testing your Flutter app, check out  Plugins in Flutter tests.<topic_end><topic_start>Types of plugin testsTo see examples of each of these types of tests, you cancreate a new plugin from the plugin templateand look in the indicated directories.Dart unit tests and widget tests.These tests allow you to test the Dart portion of your pluginjust as you would test the Dart code of a non-plugin package.However, the plugin’s native code won’t be loaded,so any calls to platform channels need to be mocked in tests.See the test directory for an example.Dart integration tests.Since integration tests run in the context of aFlutter application (the example app),they can test both the Dart and native code,as well as the interaction between them.They are also useful for unit testing web implementationcode that needs to run in a browser.These are often the most important tests for a plugin.However, Dart integration tests can’t interact with native UI,such as native dialogs or the contents of platform views.See the  example/integration_test directory for an example.Native unit tests.Just as Dart unit tests can test the Dart portionsof a plugin in isolation, native unit tests cantest the native parts in isolation.Each platform has its own native unit test system,and the tests are written in the same native languagesas the code it is testing.Native unit tests can be especially valuableif you need to mock out APIs wrapped by your plugin code,which isn’t possible in a Dart integration test.You can set up and use any native test frameworksyou are familiar with for each platform,but the following are already configured in the plugin template:Android:JUnit tests can be found in android/src/test/.iOS and macOS:XCTest tests can be found in example/ios/RunnerTests/and example/macos/RunnerTests/ respectively.These are in the example directory,not the top-level package directory,because they are run via the example app’s project.Linux and Windows:GoogleTest tests can be found in linux/test/and windows/test/, respectively.Other types of tests, which aren’t currently pre-configuredin the template, are native UI tests.Running your application under a native UI testing framework,such as Espresso or XCUITest,enables tests that interact with both native and Flutter UI elements,so can be useful if your plugin can’t be tested withoutnative UI interactions.<topic_end><topic_start>Running tests<topic_end><topic_start>Dart unit testsThese can be run like any other Flutter unit tests,either from your preferred Flutter IDE,or using flutter test.<topic_end><topic_start>Integration testsFor information on running this type of test, check out theintegration test documentation.The commands must be run in the example directory.<topic_end><topic_start>Native unit testsFor all platforms, you need to build the exampleapplication at least once before running the unit tests,to ensure that all of the platform-specific buildfiles have been created.Android JUnitIf you have the example opened as an Android projectin Android Studio, you can run the unit tests usingthe Android Studio test UI.To run the tests from the command line,use the following command in the example/android directory:iOS and macOS XCTestIf you have the example app opened in Xcode,you can run the unit tests using the Xcode Test UI.To run the tests from the command line,use the following command in the example/ios (for iOS)or example/macos (for macOS) directory:For iOS tests, you might need to first openRunner.xcworkspace in Xcode to configure code signing.Linux GoogleTestTo run the tests from the command line,use the following command in the example directory,replacing “my_plugin” with your plugin project name:If you built the example app in release mode rather thandebug, replace “debug” with “release”.Windows GoogleTestIf you have the example app opened in Visual Studio,you can run the unit tests using the Visual Studio test UI.To run the tests from the command line,use the following command in the example directory,replacing “my_plugin” with your plugin project name:If you built the example app in release mode ratherthan debug, replace “Debug” with “Release”.<topic_end><topic_start>What types of tests to addThe general advice for testing Flutter projectsapplies to plugins as well.Some extra considerations for plugin testing:Since only integration tests can test the communicationbetween Dart and the native languages,try to have at least one integration test of eachplatform channel call.If some flows can’t be tested using integrationtests—for example if they require interacting withnative UI or mocking device state—consider writing“end to end” tests of the two halves using unit tests:Native unit tests that set up the necessary mocks,then call into the method channel entry pointwith a synthesized call and validate the method response.Dart unit tests that mock the platform channel,then call the plugin’s public API and validate the results.<topic_end><topic_start>Plugins in Flutter testsinfo Note  To learn how to avoid crashes from a plugin when  testing your Flutter app, read on.  To learn how to test your plugin code, check out  Testing plugins.Almost all Flutter plugins have two parts:In fact, the native (or host) language code distinguishesa plugin package from a standard package.Building and registering the host portion of a pluginis part of the Flutter application build process,so plugins only work when your code is runningin your application, such as with flutter runor when running integration tests.When running Dart unit tests orwidget tests, the host code isn’t available.If the code you are testing calls any plugins,this often results in errors like the following:info Note  Plugin implementations that only use Dart  will work in unit tests. This is an implementation  detail of the plugin, however,  so tests shouldn’t rely on it.When unit testing code that uses plugins,there are several options to avoid this exception.The following solutions are listed in order of preference.<topic_end><topic_start>Wrap the pluginIn most cases, the best approach is to wrap plugincalls in your own API,and provide a way of mocking your own API in tests.This has several advantages:<topic_end><topic_start>Mock the plugin’s public APIIf the plugin’s API is already based on class instances,you can mock it directly, with the following caveats:<topic_end><topic_start>Mock the plugin’s platform interfaceIf the plugin is a federated plugin,it will include a platform interface that allowsregistering implementations of its internal logic.You can register a mock of that platform interfaceimplementation instead of the public API with thefollowing caveats:An example of when this might be necessary ismocking the implementation of a plugin used bya package that you rely on,rather than your own code,so you can’t change how it’s called.However, if possible,you should mock the dependency that uses the plugin instead.<topic_end><topic_start>Mock the platform channelIf the plugin uses platform channels,you can mock the platform channels usingTestDefaultBinaryMessenger.This should only be used if, for some reason,none of the methods above are available,as it has several drawbacks:Because of these limitations, TestDefaultBinaryMessengeris mainly useful in the internal testsof plugin implementations,rather than tests of code using plugins.You might also want to check outTesting plugins.<topic_end><topic_start>Debugging Flutter appsThere’s a wide variety of tools and features to help debugFlutter applications. Here are some of the available tools:<topic_end><topic_start>Other resourcesYou might find the following docs useful:<topic_end><topic_start>Debug Flutter apps from codeThis guide describes which debugging features you can enable in your code.For a full list of debugging and profiling tools, check out theDebugging page.info Note  If you are looking for a way to use GDB to remotely debug the  Flutter engine running within an Android app process,  check out flutter_gdb.<topic_end><topic_start>Add logging to your applicationinfo Note  You can view logs in DevTools’ Logging view  or in your system console. This sections  shows how to set up your logging statements.You have two options for logging for your application.Import dart:io and invoking methods onstderr and stdout. For example:<code_start>stderr.writeln('print me');<code_end>If you output too much at once, then Android might discard some log lines.To avoid this outcome,use debugPrint() from Flutter’s foundation library.This wrapper around print throttles the output to avoid the Android kerneldropping output.You can also log your app using the dart:developer log() function.This allows you to include greater granularity and more informationin the logging output.<topic_end><topic_start>Example 1<code_start>import 'dart:developer' as developer;void main() {  developer.log('log me', name: 'my.app.category');  developer.log('log me 1', name: 'my.other.category');  developer.log('log me 2', name: 'my.other.category');}<code_end>You can also pass app data to the log call.The convention for this is to use the error: namedparameter on the log() call, JSON encode the objectyou want to send, and pass the encoded string to theerror parameter.<topic_end><topic_start>Example 2<code_start>import 'dart:convert';import 'dart:developer' as developer;void main() {  var myCustomObject = MyCustomObject();  developer.log(    'log me',    name: 'my.app.category',    error: jsonEncode(myCustomObject),  );}<code_end>DevTool’s logging view interprets the JSON encoded error parameteras a data object.DevTool renders in the details view for that log entry.<topic_end><topic_start>Set breakpointsYou can set breakpoints in DevTools’ Debugger orin the built-in debugger of your IDE.To set programmatic breakpoints:Insert programmatic breakpoints using the debugger() statement.This statement takes an optional when argument.This boolean argument sets a break when the given condition resolves to true.Example 3 illustrates this.<topic_end><topic_start>Example 3<code_start>import 'dart:developer';void someFunction(double offset) {  debugger(when: offset > 30);  // ...}<code_end><topic_end><topic_start>Debug app layers using flagsEach layer of the Flutter framework provides a function to dump itscurrent state or events to the console using the debugPrint property.info Note  All of the following examples were run as macOS native apps on  a MacBook Pro M1. These will differ from any dumps your  development machine prints.lightbulb Tip  Each render object in any tree includes the first five  hexadecimal digits of its hashCode.  This hash serves as a unique identifier for that render object.<topic_end><topic_start>Print the widget treeTo dump the state of the Widgets library,call the debugDumpApp() function.<topic_end><topic_start>Example 4: Call debugDumpApp()<code_start>import 'package:flutter/material.dart';void main() {  runApp(    const MaterialApp(      home: AppHome(),    ),  );}class AppHome extends StatelessWidget {  const AppHome({super.key});  @override  Widget build(BuildContext context) {    return Material(      child: Center(        child: TextButton(          onPressed: () {            debugDumpApp();          },          child: const Text('Dump Widget Tree'),        ),      ),    );  }}<code_end>This function recursively calls the toStringDeep() method starting withthe root of the widget tree. It returns a “flattened” tree.Example 4 produces the following widget tree. It includes:Many widgets that don’t appear in your app’s source.The framework’s widgets’ build functions insert them during the build.The following tree, for example, shows _InkFeatures.That class implements part of the Material widget.It doesn’t appear anywhere in the code in Example 4.When the button changes from being pressed to being released,this invokes the debugDumpApp() function.It also coincides with the TextButton object calling setState()and thus marking itself dirty.This explains why a Flutter marks a specific object as “dirty”.When you review the widget tree, look for a line that resembles the following:If you write your own widgets, override thedebugFillProperties() method to add information.Add DiagnosticsProperty objects to the method’s argumentand call the superclass method.The toString method uses this function to fill in the widget’s description.<topic_end><topic_start>Print the render treeWhen debugging a layout issue, the Widgets layer’s tree might lack detail.The next level of debugging might require a render tree.To dump the render tree:<topic_end><topic_start>Example 5: Call debugDumpRenderTree()<code_start>import 'package:flutter/material.dart';void main() {  runApp(    const MaterialApp(      home: AppHome(),    ),  );}class AppHome extends StatelessWidget {  const AppHome({super.key});  @override  Widget build(BuildContext context) {    return Material(      child: Center(        child: TextButton(          onPressed: () {            debugDumpRenderTree();          },          child: const Text('Dump Render Tree'),        ),      ),    );  }}<code_end>When debugging layout issues, look at the size and constraints fields.The constraints flow down the tree and the sizes flow back up.In the render tree for Example 5:The RenderView, or window size, limits all render objects up to andincluding RenderPositionedBox#dc1df render objectto the size of the screen.This example sets the size to Size(800.0, 600.0)The constraints property of each render object limits the sizeof each child. This property takes the BoxConstraints render object as a value.Starting with the RenderSemanticsAnnotations#fe6b5, the constraint equalsBoxConstraints(w=800.0, h=600.0).The Center widget created the RenderPositionedBox#dc1df render objectunder the RenderSemanticsAnnotations#8187b subtree.Each child under this render object has BoxConstraints with bothminimum and maximum values. For example, RenderSemanticsAnnotations#a0a4buses BoxConstraints(0.0<=w<=800.0, 0.0<=h<=600.0).All children of the RenderPhysicalShape#8e171 render object useBoxConstraints(BoxConstraints(56.0<=w<=800.0, 28.0<=h<=600.0)).The child RenderPadding#8455f sets a padding value ofEdgeInsets(8.0, 0.0, 8.0, 0.0).This sets a left and right padding of 8 to all subsequent children ofthis render object.They now have new constraints:BoxConstraints(40.0<=w<=784.0, 28.0<=h<=600.0).This object, which the creator field tells us isprobably part of the TextButton’s definition,sets a minimum width of 88 pixels on its contents and aspecific height of 36.0. This is the TextButton class implementingthe Material Design guidelines regarding button dimensions.RenderPositionedBox#80b8d render object loosens the constraints againto center the text within the button.The RenderParagraph#59bc2 render object picks its size based onits contents.If you follow the sizes back up the tree,you see how the size of the text influences the width of all the boxesthat form the button.All parents take their child’s dimensions to size themselves.Another way to notice this is by looking at the relayoutBoundaryattribute of in the descriptions of each box.This tells you how many ancestors depend on this element’s size.For example, the innermost RenderPositionedBox line has a relayoutBoundary=up13.This means that when Flutter marks the RenderConstrainedBox as dirty,it also marks box’s 13 ancestors as dirty because the new dimensionsmight affect those ancestors.To add information to the dump if you write your own render objects,override debugFillProperties().Add DiagnosticsProperty objects to the method’s argumentthen call the superclass method.<topic_end><topic_start>Print the layer treeTo debug a compositing issue, use debugDumpLayerTree().<topic_end><topic_start>Example 6: Call debugDumpLayerTree()<code_start>import 'package:flutter/material.dart';void main() {  runApp(    const MaterialApp(      home: AppHome(),    ),  );}class AppHome extends StatelessWidget {  const AppHome({super.key});  @override  Widget build(BuildContext context) {    return Material(      child: Center(        child: TextButton(          onPressed: () {            debugDumpLayerTree();          },          child: const Text('Dump Layer Tree'),        ),      ),    );  }}<code_end>The RepaintBoundary widget creates:A RenderRepaintBoundary RenderObject in the render treeas shown in the Example 5 results.A new layer in the layer tree as shown in the Example 6results.This reduces how much needs to be repainted.<topic_end><topic_start>Print the focus treeTo debug a focus or shortcut issue, dump the focus treeusing the debugDumpFocusTree() function.The debugDumpFocusTree() method returns the focus tree for the app.The focus tree labels nodes in the following way:If your app uses the Focus widget, use the debugLabelproperty to simplify finding its focus node in the tree.You can also use the debugFocusChanges boolean property to enableextensive logging when the focus changes.<topic_end><topic_start>Example 7: Call debugDumpFocusTree()<code_start>import 'package:flutter/material.dart';void main() {  runApp(    const MaterialApp(      home: AppHome(),    ),  );}class AppHome extends StatelessWidget {  const AppHome({super.key});  @override  Widget build(BuildContext context) {    return Material(      child: Center(        child: TextButton(          onPressed: () {            debugDumpFocusTree();          },          child: const Text('Dump Focus Tree'),        ),      ),    );  }}<code_end><topic_end><topic_start>Print the semantics treeThe debugDumpSemanticsTree() function prints the semantic tree for the app.The Semantics tree is presented to the system accessibility APIs.To obtain a dump of the Semantics tree:<topic_end><topic_start>Example 8: Call debugDumpSemanticsTree()<code_start>import 'package:flutter/foundation.dart';import 'package:flutter/material.dart';import 'package:flutter/rendering.dart';void main() {  runApp(    const MaterialApp(      home: AppHome(),    ),  );}class AppHome extends StatelessWidget {  const AppHome({super.key});  @override  Widget build(BuildContext context) {    return Material(      child: Center(        child: Semantics(          button: true,          enabled: true,          label: 'Clickable text here!',          child: GestureDetector(              onTap: () {                debugDumpSemanticsTree();                if (kDebugMode) {                  print('Clicked!');                }              },              child: const Text('Click Me!', style: TextStyle(fontSize: 56))),        ),      ),    );  }}<code_end><topic_end><topic_start>Print event timingsIf you want to find out where your events happen relative to the frame’sbegin and end, you can set prints to log these events.To print the beginning and end of the frames to the console,toggle the debugPrintBeginFrameBannerand the debugPrintEndFrameBanner.The print frame banner log for Example 1To print the call stack causing the current frame to be scheduled,use the debugPrintScheduleFrameStacks flag.<topic_end><topic_start>Debug layout issuesTo debug a layout problem using a GUI, setdebugPaintSizeEnabled to true.This flag can be found in the rendering library.You can enable it at any time and affects all painting while true.Consider adding it to the top of your void main() entry point.<topic_end><topic_start>Example 9See an example in the following code:<code_start>//add import to rendering libraryimport 'package:flutter/rendering.dart';void main() {  debugPaintSizeEnabled = true;  runApp(const MyApp());}<code_end>When enabled, Flutter displays the following changes to your app:The debugPaintBaselinesEnabled flagdoes something similar but for objects with baselines.The app displays the baseline for alphabetic characters in bright greenand the baseline for ideographic characters in orange.Alphabetic characters “sit” on the alphabetic baseline,but that baseline “cuts” through the bottom of CJK characters.Flutter positions the ideographic baseline at the very bottom of the text line.The debugPaintPointersEnabled flag turns on a special mode thathighlights any objects that you tap in teal.This can help you determine if an object fails to hit test.This might happen if the object falls outside the bounds of its parentand thus not considered for hit testing in the first place.If you’re trying to debug compositor layers, consider using the following flags.Use the debugPaintLayerBordersEnabled flag to find the boundariesof each layer. This flag results in outlining each layer’s bounds in orange.Use the debugRepaintRainbowEnabled flag to display a repainted layer.Whenever a layer repaints, it overlays with a rotating set of colors.Any function or method in the Flutter framework that starts withdebug... only works in debug mode.<topic_end><topic_start>Debug animation issuesinfo Note  To debug animations with the least effort, slow them down.  To slow down the animation,  click Slow Animations in DevTools’ Inspector view.  This reduces the animation to 20% speed.  If you want more control over the amount of slowness,  use the following instructions.Set the timeDilation variable (from the schedulerlibrary) to a number greater than 1.0, for instance, 50.0.It’s best to only set this once on app startup. If youchange it on the fly, especially if you reduce it whileanimations are running, it’s possible that the frameworkwill observe time going backwards, which will probablyresult in asserts and generally interfere with your efforts.<topic_end><topic_start>Debug performance issuesinfo Note  You can achieve similar results to some of these debug  flags using DevTools. Some of the debug flags provide little benefit.  If you find a flag with functionality you would like to add to DevTools,  file an issue.Flutter provides a wide variety of top-level properties and functionsto help you debug your app at various points along thedevelopment cycle.To use these features, compile your app in debug mode.The following list highlights some of flags and one function from therendering library for debugging performance issues.To set these flags either:You can generate stack traces on demand as well.To print your own stack traces, add the debugPrintStack()function to your app.<topic_end><topic_start>Trace Dart code performanceinfo Note  You can use the DevTools Timeline events tab to perform traces.  You can also import and export trace files into the Timeline view,  but only files generated by DevTools.To perform custom performance traces andmeasure wall or CPU time of arbitrary segments of Dart codelike Android does with systrace,use dart:developer Timeline utilities.Wrap the code you want to measure in Timeline methods.<code_start>import 'dart:developer'; void main() {   Timeline.startSync('interesting function');   // iWonderHowLongThisTakes();   Timeline.finishSync(); }<code_end>To ensure that the runtime performance characteristics closely match thatof your final product, run your app in profile mode.<topic_end><topic_start>Add performance overlayinfo Note  You can toggle display of the performance overlay on  your app using the Performance Overlay button in the  Flutter inspector. If you prefer to do it in code,  use the following instructions.To enable the PerformanceOverlay widget in your code,set the showPerformanceOverlay property to true on theMaterialApp, CupertinoApp, or WidgetsAppconstructor:<topic_end><topic_start>Example 10<code_start>import 'package:flutter/material.dart';class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      showPerformanceOverlay: true,      title: 'My Awesome App',      theme: ThemeData(        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),      ),      home: const MyHomePage(title: 'My Awesome App'),    );  }}<code_end>(If you’re not using MaterialApp, CupertinoApp,or WidgetsApp, you can get the same effect by wrapping yourapplication in a stack and putting a widget on your stack that wascreated by calling PerformanceOverlay.allEnabled().)To learn how to interpret the graphs in the overlay,check out The performance overlay inProfiling Flutter performance.<topic_end><topic_start>Add widget alignment gridTo add an overlay to a Material Design baseline grid on your app tohelp verify alignments, add the debugShowMaterialGrid argument in theMaterialApp constructor.To add an overlay to non-Material applications, add a GridPaper widget.<topic_end><topic_start>Use a native language debuggerinfo Note  This guide presumes you understand general debugging,  have installed Flutter and git, and have familiarity  with the Dart language as well as one of the following  languages: Java, Kotlin, Swift, or Objective-C.If you write Flutter apps only with Dart code,you can debug your code using your IDE’s debugger.The Flutter team recommends VS Code.If you write a platform-specific plugin oruse platform-specific libraries, you can debugthat portion of your code with a native debugger.This guide shows you how you can connect twodebuggers to your Dart app, one for Dart, and one for the native code.<topic_end><topic_start>Debug Dart codeThis guide describes how to use VS Code to debug your Flutter app.You can also use your preferred IDE with theFlutter and Dart plugins installed and configured.<topic_end><topic_start>Debug Dart code using VS CodeThe following procedure explains how to use the Dart debuggerwith the default sample Flutter app.The featured components in VS Code work and appear whendebugging your own Flutter project as well.Create a basic Flutter app.Open the lib\main.dart file in the Flutter app usingVS Code.Click the bug icon().This opens the following panes in VS Code:The first time you run the debugger takes the longest.Test the debugger.a. In main.dart, click on this line:b. Press Shift + F9.   This adds a breakpoint where the   _counter variable increments.c. In the app, click the + button   to increment the counter. The app pauses.d. At this point, VS Code displays:<topic_end><topic_start>VS Code Flutter debuggerThe Flutter plugin for VS Code adds a number of componentsto the VS Code user interface.<topic_end><topic_start>Changes to VS Code interfaceWhen launched, the Flutter debugger adds debugging tools to theVS Code interface.The following screenshot and table explain the purpose of each tool.To change where the panel (in orange) appears in VS Code,go to View > Appearance > Panel Position.<topic_end><topic_start>VS Code Flutter debugging toolbarThe toolbar allows you to debug using any debugger.You can step in, out, and over Dart statements, hot reload, or resume the app.<topic_end><topic_start>Update test Flutter appFor the remainder of this guide, you need to update thetest Flutter app. This update adds native code to debug.Open the lib/main.dart file using your preferred IDE.Replace the contents of main.dart with the following code.<code_start>// Copyright 2023 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; void main() {   runApp(const MyApp()); } class MyApp extends StatelessWidget {   const MyApp({super.key});   @override   Widget build(BuildContext context) {     return MaterialApp(       title: 'URL Launcher',       theme: ThemeData(         colorSchemeSeed: Colors.purple,         brightness: Brightness.light,       ),       home: const MyHomePage(title: 'URL Launcher'),     );   } } class MyHomePage extends StatefulWidget {   const MyHomePage({super.key, required this.title});   final String title;   @override   State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> {   Future<void>? _launched;   Future<void> _launchInBrowser(Uri url) async {     if (!await launchUrl(       url,       mode: LaunchMode.externalApplication,     )) {       throw Exception('Could not launch $url');     }   }   Future<void> _launchInWebView(Uri url) async {     if (!await launchUrl(       url,       mode: LaunchMode.inAppWebView,     )) {       throw Exception('Could not launch $url');     }   }   Widget _launchStatus(BuildContext context, AsyncSnapshot<void> snapshot) {     if (snapshot.hasError) {       return Text('Error: ${snapshot.error}');     } else {       return const Text('');     }   }   @override   Widget build(BuildContext context) {     final Uri toLaunch = Uri(         scheme: 'https',         host: 'docs.flutter.dev',         path: 'testing/native-debugging');     return Scaffold(       appBar: AppBar(         title: Text(widget.title),       ),       body: Center(         child: Column(           mainAxisAlignment: MainAxisAlignment.center,           children: <Widget>[             Padding(               padding: const EdgeInsets.all(16),               child: Text(toLaunch.toString()),             ),             FilledButton(               onPressed: () => setState(() {                 _launched = _launchInBrowser(toLaunch);               }),               child: const Text('Launch in browser'),             ),             const Padding(padding: EdgeInsets.all(16)),             FilledButton(               onPressed: () => setState(() {                 _launched = _launchInWebView(toLaunch);               }),               child: const Text('Launch in app'),             ),             const Padding(padding: EdgeInsets.all(16.0)),             FutureBuilder<void>(future: _launched, builder: _launchStatus),           ],         ),       ),     );   } }<code_end>To add the url_launcher package as a dependency,run flutter pub add:To check what changed with the codebase:In Linux or macOS, run this find command.In Windows, run this command in the command prompt.Installing url_launcher added config files and code filesfor all target platforms in the Flutter app directory.<topic_end><topic_start>Debug Dart and native language code at the same timeThis section explains how to debug the Dart code in your Flutter appand any native code with its regular debugger.This capability allows you to leverage Flutter’s hot reloadwhen editing native code.<topic_end><topic_start>Debug Dart and Android code using Android StudioTo debug native Android code, you need a Flutter app that containsAndroid code. In this section, you learn how to connectthe Dart, Java, and Kotlin debuggers to your app.You don’t need VS Code to debug both Dart and Android code.This guide includes the VS Code instructions to be consistentwith the Xcode and Visual Studio guides.These section uses the same example Flutter url_launcher app createdin Update test Flutter app.info Note  If you want to use the GNU Project Debugger to debug the  Flutter engine running within an Android app process,  check out flutter_gdb.<topic_end><topic_start>Build the Android version of the Flutter app in the TerminalTo generate the needed Android platform dependencies,run the flutter build command.<topic_end><topic_start>Start debugging with VS Code firstIf you use VS Code to debug most of your code, start with this section.To open the Flutter app directory, go toFile >Open Folder… and choose the my_app directory.Open the lib/main.dart file.If you can build an app for more than one device,you must select the device first.Go toView >Command Palette…You can also press Ctrl / Cmd +Shift + P.Type flutter select.Click the Flutter: Select Device command.Choose your target device.Click the debug icon().This opens the Debug pane and launches the app.Wait for the app to launch on the device and for the debug pane toindicate Connected.The debugger takes longer to launch the first time.Subsequent launches start faster.This Flutter app contains two buttons:<topic_end><topic_start>Attach to the Flutter process in Android StudioClick the Attach debugger to Android process button.()lightbulb Tip   If this button doesn’t appear in the Projects menu bar, verify that   you opened Flutter application project but not a Flutter plugin.The process dialog displays one entry for each connected device.Select show all processes to display available processes for eachdevice.Choose the process to which you want to attach.For this guide, select the com.example.my_app processusing the Emulator Pixel_5_API_33.Locate the tab for Android Debugger in the Debug pane.In the Project pane, expandmy_app_android >android >app >src >main >java >io.flutter plugins.Double click GeneratedProjectRegistrant to open theJava code in the Edit pane.At the end of this procedure, both the Dart and Android debuggers interactwith the same process.Use either, or both, to set breakpoints, examine stack, resume executionand the like. In other words, debug!<topic_end><topic_start>Start debugging with Android Studio firstIf you use Android Studio to debug most of your code, start with this section.To open the Flutter app directory, go toFile >Open… and choose the my_app directory.Open the lib/main.dart file.Choose a virtual Android device.Go to the toolbar, open the leftmost dropdown menu, and click onOpen Android Emulator: <device>.You can choose any installed emulator that’s doesn’t include arm64.From that same menu, select the virtual Android device.From the toolbar, click Run ‘main.dart’.You can also press Ctrl + Shift + R.After the app displays in the emulator, continue to the next step.Click the Attach debugger to Android process button.()lightbulb Tip   If this button doesn’t appear in the Projects menu bar, verify that   you opened Flutter application project but not a Flutter plugin.The process dialog displays one entry for each connected device.Select show all processes to display available processes for eachdevice.Choose the process to which you want to attach.For this guide, select the com.example.my_app processusing the Emulator Pixel_5_API_33.Locate the tab for Android Debugger in the Debug pane.In the Project pane, expandmy_app_android >android >app >src >main >java >io.flutter plugins.Double click GeneratedProjectRegistrant to open theJava code in the Edit pane.At the end of this procedure, both the Dart and Android debuggers interactwith the same process.Use either, or both, to set breakpoints, examine stack, resume executionand the like. In other words, debug!<topic_end><topic_start>Debug Dart and iOS code using XcodeTo debug iOS code, you need a Flutter app that contains iOS code.In this section, you learn to connect two debuggers to your app:Flutter via VS Code and Xcode. You need to run both VS Code and Xcode.These section uses the same example Flutter url_launcher app createdin Update test Flutter app.<topic_end><topic_start>Build the iOS version of the Flutter app in the TerminalTo generate the needed iOS platform dependencies,run the flutter build command.<topic_end><topic_start>Start debugging with VS Code firstIf you use VS Code to debug most of your code, start with this section.<topic_end><topic_start>Start the Dart debugger in VS CodeTo open the Flutter app directory, go toFile >Open Folder… and choose the my_app directory.Open the lib/main.dart file.If you can build an app for more than one device,you must select the device first.Go toView >Command Palette…You can also press Ctrl / Cmd +Shift + P.Type flutter select.Click the Flutter: Select Device command.Choose your target device.Click the debug icon().This opens the Debug pane and launches the app.Wait for the app to launch on the device and for the debug pane toindicate Connected.The debugger takes longer to launch the first time.Subsequent launches start faster.This Flutter app contains two buttons:<topic_end><topic_start>Attach to the Flutter process in XcodeTo attach to the Flutter app, go toDebug >Attach to Process >Runner.Runner should be at the top of the Attach to Process menuunder the Likely Targets heading.<topic_end><topic_start>Start debugging with Xcode firstIf you use Xcode to debug most of your code, start with this section.<topic_end><topic_start>Start the Xcode debuggerOpen ios/Runner.xcworkspace from your Flutter app directory.Select the correct device using the Scheme menu in the toolbar.If you have no preference, choose iPhone Pro 14.Run this Runner as a normal app in Xcode.When the run completes, the Debug area at the bottom of Xcode displays a message with the Dart VM service URI. It resembles the following response:Copy the Dart VM service URI.<topic_end><topic_start>Attach to the Dart VM in VS CodeTo open the command palette, go to View >Command Palette…You can also press Cmd + Shift + P.Type debug.Click the Debug: Attach to Flutter on Device command.In the Paste an VM Service URI box, paste the URI you copied from Xcode and press Enter.<topic_end><topic_start>Debug Dart and macOS code using XcodeTo debug macOS code, you need a Flutter app that contains macOS code.In this section, you learn to connect two debuggers to your app:Flutter via VS Code and Xcode. You need to run both VS Code and Xcode.These section uses the same example Flutter url_launcher app createdin Update test Flutter app.<topic_end><topic_start>Build the macOS version of the Flutter app in the TerminalTo generate the needed macOS platform dependencies,run the flutter build command.<topic_end><topic_start>Start debugging with VS Code first<topic_end><topic_start>Start the debugger in VS CodeTo open the Flutter app directory, go toFile >Open Folder… and choose the my_app directory.Open the lib/main.dart file.If you can build an app for more than one device,you must select the device first.Go toView >Command Palette…You can also press Ctrl / Cmd +Shift + P.Type flutter select.Click the Flutter: Select Device command.Choose your target device.Click the debug icon().This opens the Debug pane and launches the app.Wait for the app to launch on the device and for the debug pane toindicate Connected.The debugger takes longer to launch the first time.Subsequent launches start faster.This Flutter app contains two buttons:<topic_end><topic_start>Attach to the Flutter process in XcodeTo attach to the Flutter app, go toDebug >Attach to Process >Runner.Runner should be at the top of the Attach to Process menuunder the Likely Targets heading.<topic_end><topic_start>Start debugging with Xcode first<topic_end><topic_start>Start the debugger in XcodeOpen macos/Runner.xcworkspace from your Flutter app directory.Run this Runner as a normal app in Xcode.When the run completes, the Debug area at the bottom of Xcode displaysa message with the Dart VM service URI. It resembles the following response:Copy the Dart VM service URI.<topic_end><topic_start>Attach to the Dart VM in VS CodeTo open the command palette, go to View > Command Palette…You can also press Cmd + Shift + P.Type debug.Click the Debug: Attach to Flutter on Device command.In the Paste an VM Service URI box, paste the URI you copiedfrom Xcode and press Enter.<topic_end><topic_start>Debug Dart and C++ code using Visual StudioTo debug C++ code, you need a Flutter app that contains C++ code.In this section, you learn to connect two debuggers to your app:Flutter via VS Code and Visual Studio.You need to run both VS Code and Visual Studio.These section uses the same example Flutter url_launcher app createdin Update test Flutter app.<topic_end><topic_start>Build the Windows version of the Flutter app in PowerShell or the Command PromptTo generate the needed Windows platform dependencies,run the flutter build command.<topic_end><topic_start>Start debugging with VS Code firstIf you use VS Code to debug most of your code, start with this section.<topic_end><topic_start>Start the debugger in VS CodeTo open the Flutter app directory, go toFile >Open Folder… and choose the my_app directory.Open the lib/main.dart file.If you can build an app for more than one device,you must select the device first.Go toView >Command Palette…You can also press Ctrl / Cmd +Shift + P.Type flutter select.Click the Flutter: Select Device command.Choose your target device.Click the debug icon().This opens the Debug pane and launches the app.Wait for the app to launch on the device and for the debug pane toindicate Connected.The debugger takes longer to launch the first time.Subsequent launches start faster.This Flutter app contains two buttons:<topic_end><topic_start>Attach to the Flutter process in Visual StudioTo open the project solution file, go toFile >Open >Project/Solution…You can also press Ctrl + Shift + O.Choose the build/windows/my_app.sln file in your Flutter app directory.Go to Debug > Attach to Process.You can also press Ctrl + Alt + P.From the Attach to Process dialog box, choose my_app.exe.Visual Studio starts monitoring the Flutter app.<topic_end><topic_start>Start debugging with Visual Studio firstIf you use Visual Studio to debug most of your code, start with this section.<topic_end><topic_start>Start the local Windows debuggerTo open the project solution file, go toFile >Open >Project/Solution…You can also press Ctrl + Shift + O.Choose the build/windows/my_app.sln file in your Flutter app directory.Set my_app as the startup project.In the Solution Explorer, right-click on my_app and selectSet as Startup Project.Click Local Windows Debugger to start debugging.You can also press F5.When the Flutter app has started, a console window displaysa message with the Dart VM service URI. It resembles the following response:Copy the Dart VM service URI.<topic_end><topic_start>Attach to the Dart VM in VS CodeTo open the command palette, go toView >Command Palette…You can also press Cmd + Shift + P.Type debug.Click the Debug: Attach to Flutter on Device command.In the Paste an VM Service URI box, paste the URI you copiedfrom Visual Studio and press Enter.<topic_end><topic_start>ResourcesCheck out the following resources on debugging Flutter, iOS, Android,macOS and Windows:<topic_end><topic_start>Flutter<topic_end><topic_start>AndroidYou can find the following debugging resources ondeveloper.android.com.<topic_end><topic_start>iOS and macOSYou can find the following debugging resources ondeveloper.apple.com.<topic_end><topic_start>WindowsYou can find debugging resources on Microsoft Learn.<topic_end><topic_start>Flutter's build modesThe Flutter tooling supports three modes when compiling your app,and a headless mode for testing.You choose a compilation mode depending on where you are inthe development cycle. Are you debugging your code? Do youneed profiling information? Are you ready to deploy your app?A quick summary for when to use which mode is as follows:The rest of the page details these modes.<topic_end><topic_start>DebugIn debug mode, the app is set up for debugging on the physicaldevice, emulator, or simulator.Debug mode for mobile apps mean that:Debug mode for a web app means that:By default, flutter run compiles to debug mode.Your IDE supports this mode. Android Studio,for example, provides a Run > Debug… menu option,as well as a green bug icon overlaid with a small triangleon the project page.info Note<topic_end><topic_start>ReleaseUse release mode for deploying the app, when you want maximumoptimization and minimal footprint size. For mobile, release mode(which is not supported on the simulator or emulator), means that:Release mode for a web app means that:The command flutter run --release compiles to release mode.Your IDE supports this mode. Android Studio, for example,provides a Run > Run… menu option, as well as a triangulargreen run button icon on the project page.You can compile to release mode for a specific targetwith flutter build <target>. For a list of supported targets,use flutter help build.For more information, see the docs on releasingiOS and Android apps.<topic_end><topic_start>ProfileIn profile mode, some debugging ability is maintained—enoughto profile your app’s performance. Profile mode is disabled onthe emulator and simulator, because their behavior is not representativeof real performance. On mobile, profile mode is similar to release mode,with the following differences:Profile mode for a web app means that:Your IDE supports this mode. Android Studio, for example,provides a Run > Profile… menu option.The command flutter run --profile compiles to profile mode.info Note  Use the DevTools suite to profile your app’s performance.For more information on the build modes, seeFlutter’s build modes.<topic_end><topic_start>Common Flutter errors<topic_end><topic_start>IntroductionThis page explains several frequently-encountered Flutterframework errors (including layout errors) and gives suggestionson how to resolve them.This is a living document with more errors to be added infuture revisions, and your contributions are welcomed.Feel free to open an issue or submit a pull request tomake this page more useful to you and the Flutter community.<topic_end><topic_start>‘A RenderFlex overflowed…’RenderFlex overflow is one of the most frequentlyencountered Flutter framework errors,and you’ve probably run into it already.What does the error look like?When it happens, yellow and black stripes appear,indicating the area of overflow in the app UI.In addition, an error message displays in the debug console:How might you run into this error?The error often occurs when a Column or Row has achild widget that isn’t constrained in its size.For example,the code snippet below demonstrates a common scenario:<code_start>Widget build(BuildContext context) {  return Row(    children: [      const Icon(Icons.message),      Column(        mainAxisSize: MainAxisSize.min,        crossAxisAlignment: CrossAxisAlignment.start,        children: [          Text('Title', style: Theme.of(context).textTheme.headlineMedium),          const Text(            'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed '            'do eiusmod tempor incididunt ut labore et dolore magna '            'aliqua. Ut enim ad minim veniam, quis nostrud '            'exercitation ullamco laboris nisi ut aliquip ex ea '            'commodo consequat.',          ),        ],      ),    ],  );}<code_end>In the above example,the Column tries to be wider than the space the Row(its parent) can allocate to it, causing an overflow error.Why does the Column try to do that?To understand this layout behavior, you need to knowhow Flutter framework performs layout:“To perform layout, Flutter walks the render tree in a depth-first traversaland passes down size constraints from parent to child… Children respond bypassing up a size to their parent object within the constraints the parentestablished.” – Flutter architectural overviewIn this case, the Row widget doesn’t constrain thesize of its children, nor does the Column widget.Lacking constraints from its parent widget, the secondText widget tries to be as wide as all the charactersit needs to display. The self-determined width of theText widget then gets adopted by the Column, whichclashes with the maximum amount of horizontal space its parent,the Row widget, can provide.How to fix it?Well, you need to make sure the Column won’t attemptto be wider than it can be. To achieve this,you need to constrain its width. One way to do it is towrap the Column in an Expanded widget:<code_start>return const Row(  children: [    Icon(Icons.message),    Expanded(      child: Column(          // code omitted          ),    ),  ],);<code_end>Another way is to wrap the Column in a Flexible widgetand specify a flex factor. In fact,the Expanded widget is equivalent to the Flexible widgetwith a flex factor of 1.0, as its source code shows.To further understand how to use the Flex widget in Flutter layouts,check out this 90-second Widget of the Week videoon the Flexible widget.Further information:The resources linked below provide further information about this error.<topic_end><topic_start>‘RenderBox was not laid out’While this error is pretty common,it’s often a side effect of a primary erroroccurring earlier in the rendering pipeline.What does the error look like?The message shown by the error looks like this:How might you run into this error?Usually, the issue is related to violation of box constraints,and it needs to be solved by providing more informationto Flutter about how you’d like to constrain the widgets in question.You can learn more about how constraints workin Flutter on the Understanding constraints page.The RenderBox was not laid out error is oftencaused by one of two other errors:<topic_end><topic_start>‘Vertical viewport was given unbounded height’This is another common layout error you could run intowhile creating a UI in your Flutter app.What does the error look like?The message shown by the error looks like this:How might you run into this error?The error is often caused when a ListView(or other kinds of scrollable widgets such as GridView)is placed inside a Column. A ListView takes allthe vertical space available to it,unless it’s constrained by its parent widget.However, a Column doesn’t impose any constrainton its children’s height by default.The combination of the two behaviors leads to the failure ofdetermining the size of the ListView.<code_start>Widget build(BuildContext context) {  return Center(    child: Column(      children: <Widget>[        const Text('Header'),        ListView(          children: const <Widget>[            ListTile(              leading: Icon(Icons.map),              title: Text('Map'),            ),            ListTile(              leading: Icon(Icons.subway),              title: Text('Subway'),            ),          ],        ),      ],    ),  );}<code_end>How to fix it?To fix this error, specify how tall the ListView should be.To make it as tall as the remaining space in the Column,wrap it using an Expanded widget (as shown in the following example).Otherwise, specify an absolute height using a SizedBoxwidget or a relative height using a Flexible widget.<code_start>Widget build(BuildContext context) {  return Center(    child: Column(      children: <Widget>[        const Text('Header'),        Expanded(          child: ListView(            children: const <Widget>[              ListTile(                leading: Icon(Icons.map),                title: Text('Map'),              ),              ListTile(                leading: Icon(Icons.subway),                title: Text('Subway'),              ),            ],          ),        ),      ],    ),  );}<code_end>Further information:The resources linked below providefurther information about this error.<topic_end><topic_start>‘An InputDecorator…cannot have an unbounded width’The error message suggests that it’s also relatedto box constraints, which are important to understandto avoid many of the most common Flutter framework errors.What does the error look like?The message shown by the error looks like this:How might you run into the error?This error occurs, for example, when a Row contains aTextFormField or a TextField but the latter hasno width constraint.<code_start>Widget build(BuildContext context) {  return MaterialApp(    home: Scaffold(      appBar: AppBar(        title: const Text('Unbounded Width of the TextField'),      ),      body: const Row(        children: [          TextField(),        ],      ),    ),  );}<code_end>How to fix it?As suggested by the error message,fix this error by constraining the text fieldusing either an Expanded or SizedBox widget.The following example demonstrates using an Expanded widget:<code_start>Widget build(BuildContext context) {  return MaterialApp(    home: Scaffold(      appBar: AppBar(        title: const Text('Unbounded Width of the TextField'),      ),      body: Row(        children: [          Expanded(child: TextFormField()),        ],      ),    ),  );}<code_end><topic_end><topic_start>‘Incorrect use of ParentData widget’This error is about missing an expected parent widget.What does the error look like?The message shown by the error looks like this:How might you run into the error?While Flutter’s widgets are generally flexiblein how they can be composed together in a UI,a small subset of those widgets expect specific parent widgets.When this expectation can’t be satisfied in your widget tree,you’re likely to encounter this error.Here is an incomplete list of widgets that expectspecific parent widgets within the Flutter framework.Feel free to submit a PR (using the doc icon inthe top right corner of the page) to expand this list.How to fix it?The fix should be obvious once you knowwhich parent widget is missing.<topic_end><topic_start>‘setState called during build’The build method in your Flutter code isn’ta good place to call setState,either directly or indirectly.What does the error look like?When the error occurs,the following message is displayed in the console:How might you run into the error?In general, this error occurs when the setStatemethod is called within the build method.A common scenario where this error occurs is whenattempting to trigger a Dialog from within thebuild method. This is often motivated by the need toimmediately show information to the user,but setState should never be called from a build method.The following snippet seems to be a common culprit of this error:<code_start>Widget build(BuildContext context) {  // Don't do this.  showDialog(      context: context,      builder: (context) {        return const AlertDialog(          title: Text('Alert Dialog'),        );      });  return const Center(    child: Column(      children: <Widget>[        Text('Show Material Dialog'),      ],    ),  );}<code_end>This code doesn’t make an explicit call to setState,but it’s called by showDialog.The build method isn’t the right place to callshowDialog because build can be called by theframework for every frame, for example, during an animation.How to fix it?One way to avoid this error is to use the Navigator APIto trigger the dialog as a route. In the following example,there are two pages. The second page has adialog to be displayed upon entry.When the user requests the second page byclicking a button on the first page,the Navigator pushes two routes–onefor the second page and another for the dialog.<code_start>class FirstScreen extends StatelessWidget {  const FirstScreen({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(        title: const Text('First Screen'),      ),      body: Center(        child: ElevatedButton(          child: const Text('Launch screen'),          onPressed: () {            // Navigate to the second screen using a named route.            Navigator.pushNamed(context, '/second');            // Immediately show a dialog upon loading the second screen.            Navigator.push(              context,              PageRouteBuilder(                barrierDismissible: true,                opaque: false,                pageBuilder: (_, anim1, anim2) => const MyDialog(),              ),            );          },        ),      ),    );  }}<code_end><topic_end><topic_start>The ScrollController is attached to multiple scroll viewsThis error can occur when multiple scrollingwidgets (such as ListView) appear on thescreen at the same time. It’s more likely forthis error to occur on a web or desktop app,than a mobile app since it’s rare to encounterthis scenario on mobile.For more information and to learn how to fix,check out the following video onPrimaryScrollController:<topic_end><topic_start>ReferencesTo learn more about how to debug errors,especially layout errors in Flutter,check out the following resources:<topic_end><topic_start>Handling errors in FlutterThe Flutter framework catches errors that occur during callbackstriggered by the framework itself, including errors encounteredduring the build, layout, and paint phases. Errors that don’t occurwithin Flutter’s callbacks can’t be caught by the framework,but you can handle them by setting up an error handler on thePlatformDispatcher.All errors caught by Flutter are routed to theFlutterError.onError handler. By default,this calls FlutterError.presentError,which dumps the error to the device logs.When running from an IDE, the inspector overrides thisbehavior so that errors can also be routed to the IDE’sconsole, allowing you to inspect theobjects mentioned in the message.info Note  Consider calling FlutterError.presentError  from your custom error handler in order to see  the logs in the console as well.When an error occurs during the build phase,the ErrorWidget.builder callback isinvoked to build the widget that is usedinstead of the one that failed. By default,in debug mode this shows an error message in red,and in release mode this shows a gray background.When errors occur without a Flutter callback on the call stack,they are handled by the PlatformDispatcher’s error callback. By default,this only prints errors and does nothing else.You can customize these behaviors,typically by setting them to values inyour void main() function.Below each error type handling is explained. At the bottomthere’s a code snippet which handles all types of errors. Eventhough you can just copy-paste the snippet, we recommend youto first get acquainted with each of the error types.<topic_end><topic_start>Errors caught by FlutterFor example, to make your application quit immediately any time anerror is caught by Flutter in release mode, you could use thefollowing handler:<code_start>import 'dart:io';import 'package:flutter/foundation.dart';import 'package:flutter/material.dart';void main() {  FlutterError.onError = (details) {    FlutterError.presentError(details);    if (kReleaseMode) exit(1);  };  runApp(const MyApp());}// rest of `flutter create` code...<code_end>info Note  The top-level kReleaseMode constant indicates  whether the app was compiled in release mode.This handler can also be used to report errors to a logging service.For more details, see our cookbook chapter forreporting errors to a service.<topic_end><topic_start>Define a custom error widget for build phase errorsTo define a customized error widget that displays wheneverthe builder fails to build a widget, use MaterialApp.builder.<code_start>class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      builder: (context, widget) {        Widget error = const Text('...rendering error...');        if (widget is Scaffold || widget is Navigator) {          error = Scaffold(body: Center(child: error));        }        ErrorWidget.builder = (errorDetails) => error;        if (widget != null) return widget;        throw StateError('widget is null');      },    );  }}<code_end><topic_end><topic_start>Errors not caught by FlutterConsider an onPressed callback that invokes an asynchronous function,such as MethodChannel.invokeMethod (or pretty much any plugin).For example:<code_start>OutlinedButton(  child: const Text('Click me!'),  onPressed: () async {    const channel = MethodChannel('crashy-custom-channel');    await channel.invokeMethod('blah');  },)<code_end>If invokeMethod throws an error, it won’t be forwarded to FlutterError.onError.Instead, it’s forwarded to the PlatformDispatcher.To catch such an error, use PlatformDispatcher.instance.onError.<code_start>import 'package:flutter/material.dart';import 'dart:ui';void main() {  MyBackend myBackend = MyBackend();  PlatformDispatcher.instance.onError = (error, stack) {    myBackend.sendError(error, stack);    return true;  };  runApp(const MyApp());}<code_end><topic_end><topic_start>Handling all types of errorsSay you want to exit application on any exception and to displaya custom error widget whenever a widget building fails - you can baseyour errors handling on next code snippet:<code_start>import 'package:flutter/material.dart';import 'dart:ui';Future<void> main() async {  await myErrorsHandler.initialize();  FlutterError.onError = (details) {    FlutterError.presentError(details);    myErrorsHandler.onErrorDetails(details);  };  PlatformDispatcher.instance.onError = (error, stack) {    myErrorsHandler.onError(error, stack);    return true;  };  runApp(const MyApp());}class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      builder: (context, widget) {        Widget error = const Text('...rendering error...');        if (widget is Scaffold || widget is Navigator) {          error = Scaffold(body: Center(child: error));        }        ErrorWidget.builder = (errorDetails) => error;        if (widget != null) return widget;        throw StateError('widget is null');      },    );  }}<code_end><topic_end><topic_start>Report errors to a serviceWhile one always tries to create apps that are free of bugs,they’re sure to crop up from time to time.Since buggy apps lead to unhappy users and customers,it’s important to understand how often your usersexperience bugs and where those bugs occur.That way, you can prioritize the bugs with thehighest impact and work to fix them.How can you determine how often your users experiences bugs?Whenever an error occurs, create a report containing theerror that occurred and the associated stacktrace.You can then send the report to an error trackingservice, such as Bugsnag, Datadog,Firebase Crashlytics, Rollbar, or Sentry.The error tracking service aggregates all of the crashes your usersexperience and groups them together. This allows you to know how often yourapp fails and where the users run into trouble.In this recipe, learn how to report errors to theSentry crash reporting service usingthe following steps:<topic_end><topic_start>1. Get a DSN from SentryBefore reporting errors to Sentry, you need a “DSN” to uniquely identifyyour app with the Sentry.io service.To get a DSN, use the following steps:<topic_end><topic_start>2. Import the Sentry packageImport the sentry_flutter package into the app.The sentry package makes it easier to senderror reports to the Sentry error tracking service.To add the sentry_flutter package as a dependency,run flutter pub add:<topic_end><topic_start>3. Initialize the Sentry SDKInitialize the SDK to capture different unhandled errors automatically:<code_start>import 'package:flutter/widgets.dart';import 'package:sentry_flutter/sentry_flutter.dart';Future<void> main() async {  await SentryFlutter.init(    (options) => options.dsn = 'https://example@sentry.io/example',    appRunner: () => runApp(const MyApp()),  );}<code_end>Alternatively, you can pass the DSN to Flutter using the dart-define tag:<topic_end><topic_start>What does that give me?This is all you need for Sentry to capture unhandled errors in Dart and native layers.This includes Swift, Objective-C, C, and C++ on iOS, and Java, Kotlin, C, and C++ on Android.<topic_end><topic_start>4. Capture errors programmaticallyBesides the automatic error reporting that Sentry generates byimporting and initializing the SDK,you can use the API to report errors to Sentry:<code_start>await Sentry.captureException(exception, stackTrace: stackTrace);<code_end>For more information, see the Sentry API docs on pub.dev.<topic_end><topic_start>Learn moreExtensive documentation about using the Sentry SDK can be found on Sentry’s site.<topic_end><topic_start>Complete exampleTo view a working example,see the Sentry flutter example app.<topic_end><topic_start>PerformanceFlutter performance basicsinfo Note  If your app has a performance issue and you are  trying to debug it, check out the DevTool’s page  on Using the Performance view.What is performance? Why is performance important? How do I improve performance?Our goal is to answer those three questions (mainly the third one), andanything related to them. This document should serve as the single entrypoint or the root node of a tree of resources that addresses any questionsthat you have about performance.The answers to the first two questions are mostly philosophical,and not as helpful to many developers who visit this page with specificperformance issues that need to be solved.Therefore, the answers to thosequestions are in the appendix.To improve performance, you first need metrics: some measurable numbers toverify the problems and improvements.In the metrics page,you’ll see which metrics are currently used,and which tools and APIs are available to get the metrics.There is a list of Frequently asked questions,so you can find out if the questions you have or the problems you’re havingwere already answered or encountered, and whether there are existing solutions.(Alternatively, you can check the Flutter GitHub issue database using theperformance label.)Finally, the performance issues are divided into four categories. Theycorrespond to the four labels that are used in the Flutter GitHub issuedatabase: “perf: speed”, “perf: memory”,“perf: app size”, “perf: energy”.The rest of the content is organized using those four categories.<topic_end><topic_start>SpeedAre your animations janky (not smooth)? Learn how toevaluate and fix rendering issues.Improving rendering performance<topic_end><topic_start>App sizeHow to measure your app’s size. The smaller the size,the quicker it is to download.Measuring your app’s size<topic_end><topic_start>Impeller rendering engine<topic_end><topic_start>What is Impeller?Impeller provides a new rendering runtime for Flutter.The Flutter team’s believes this solves Flutter’searly-onset jank issue.Impeller precompiles a smaller, simpler set of shadersat Engine build time so they don’t compile at runtime.For a video introduction to Impeller, check out the followingtalk from Google I/O 2023.Introducing Impeller - Flutter’s new rendering engineImpeller has the following objectives:<topic_end><topic_start>AvailabilityWhere can you use Impeller?<topic_end><topic_start>iOSFlutter enables Impeller by default on iOS.To disable Impeller on iOS when debugging,pass --no-enable-impeller to the flutter run command.To disable Impeller on iOS when deploying your app,add the following tags under the top-level <dict> tag in yourapp’s Info.plist file.The team continues to improve iOS support.If you encounter performance or fidelity issueswith Impeller on iOS, file an issue in the GitHub tracker.Prefix the issue title with [Impeller] andinclude a small reproducible test case.<topic_end><topic_start>macOSImpeller is available for macOS in preview as ofthe Flutter 3.13 stable release.It continues to be in preview as of the 3.19 release.To enable Impeller on macOS when debugging,pass --enable-impeller to the flutter run command.To enable Impeller on macOS when deploying your app,add the following tags under the top-level <dict> tag in yourapp’s Info.plist file.<topic_end><topic_start>AndroidAs of Flutter 3.16, Impeller is available behinda flag on Android devices that support Vulkan.It continues to be in preview as of the 3.19 release.Does your device support Vulkan?  You can determine whether your Android device  supports Vulkan at checking for Vulkan support.You can try Impeller on Vulkan-capable Android devicesby passing --enable-impeller to flutter run:Or, you can add the following setting to your project’sAndroidManifest.xml file under the <application> tag:<topic_end><topic_start>Bugs and issuesFor the full list of Impeller’s known bugsand missing features,the most up-to-date information is on theImpeller project board on GitHub.The team continues to improve Impeller support.If you encounter performance or fidelity issueswith Impeller on any platform,file an issue in the GitHub tracker.Prefix the issue title with [Impeller] andinclude a small reproducible test case.Please include the following information whensubmitting an issue for Impeller:<topic_end><topic_start>ArchitectureTo learn more details about Impeller’s design and architecture,check out the README.md file in the source tree.<topic_end><topic_start>Additional Information<topic_end><topic_start>Performance best practicesinfo Note  To learn how to use the Performance View  (part of Flutter DevTools)  for debugging performance issues,  see Using the Performance view.Generally, Flutter applications are performant by default,so you only need to avoid common pitfalls to get excellentperformance. These best practice recommendations will help youwrite the most performant Flutter app possible.info Note  If you are writing web apps in Flutter, you might be interested  in a series of articles, written by the Flutter Material team,  after they modified the Flutter Gallery app to make it more  performant on the web:How do you design a Flutter app to most efficientlyrender your scenes? In particular, how do you ensurethat the painting code generated by theframework is as efficient as possible?Some rendering and layout operations are knownto be slow, but can’t always be avoided.They should be used thoughtfully,following the guidance below.<topic_end><topic_start>Minimize expensive operationsSome operations are more expensive than others,meaning that they consume more resources.Obviously, you want to only use these operationswhen necessary. How you design and implement yourapp’s UI can have a big impact on how efficiently it runs.<topic_end><topic_start>Control build() costHere are some things to keep in mind when designing your UI:For more information, check out:<topic_end><topic_start>Use saveLayer() thoughtfullySome Flutter code uses saveLayer(), an expensive operation,to implement various visual effects in the UI.Even if your code doesn’t explicitly call saveLayer(),other widgets or packages that you use might call it behind the scenes.Perhaps your app is calling saveLayer() more than necessary;excessive calls to saveLayer() can cause jank.<topic_end><topic_start>Why is saveLayer expensive?Calling saveLayer() allocates an offscreen bufferand drawing content into the offscreen buffer mighttrigger a render target switch.The GPU wants to run like a firehose,and a render target switch forces the GPUto redirect that stream temporarily and thendirect it back again. On mobile GPUs this isparticularly disruptive to rendering throughput.<topic_end><topic_start>When is saveLayer required?At runtime, if you need to dynamically display various shapescoming from a server (for example), each with some transparency,that might (or might not) overlap,then you pretty much have to use saveLayer().<topic_end><topic_start>Debugging calls to saveLayerHow can you tell how often your app calls saveLayer(),either directly or indirectly?The saveLayer() method triggersan event on the DevTools timeline; learn whenyour scene uses saveLayer by checking thePerformanceOverlayLayer.checkerboardOffscreenLayersswitch in the DevTools Performance view.<topic_end><topic_start>Minimizing calls to saveLayerCan you avoid calls to saveLayer?It might require rethinking of how youcreate your visual effects:Note to package owners:  As a best practice, consider providing documentation  for when saveLayer might be necessary for your package,  how it might be avoided, and when it can’t be avoided.Other widgets that might trigger saveLayer()and are potentially costly:<topic_end><topic_start>Minimize use of opacity and clippingOpacity is another expensive operation, as is clipping.Here are some tips you might find to be useful:<topic_end><topic_start>Implement grids and lists thoughtfullyHow your grids and lists are implementedmight be causing performance problems for your app.This section describes an important bestpractice when creating grids and lists,and how to determine whether your app usesexcessive layout passes.<topic_end><topic_start>Be lazy!When building a large grid or list,use the lazy builder methods, with callbacks.That ensures that only the visible portion of thescreen is built at startup time.For more information and examples, check out:<topic_end><topic_start>Avoid intrinsicsFor information on how intrinsic passes might be causingproblems with your grids and lists, see the next section.<topic_end><topic_start>Minimize layout passes caused by intrinsic operationsIf you’ve done much Flutter programming, you areprobably familiar with how layout and constraints workwhen creating your UI. You might even have memorized Flutter’sbasic layout rule: Constraints go down. Sizes go up.Parent sets position.For some widgets, particularly grids and lists,the layout process can be expensive.Flutter strives to perform just one layout passover the widgets but, sometimes,a second pass (called an intrinsic pass) is needed,and that can slow performance.<topic_end><topic_start>What is an intrinsic pass?An intrinsic pass happens when, for example,you want all cells to have the sizeof the biggest or smallest cell (or somesimilar calculation that requires polling all cells).For example, consider a large grid of Cards.A grid should have uniformly sized cells,so the layout code performs a pass,starting from the root of the grid (in the widget tree),asking each card in the grid (not just thevisible cards) to returnits intrinsic size—the sizethat the widget prefers, assuming no constraints.With this information,the framework determines a uniform cell size,and re-visits all grid cells a second time,telling each card what size to use.<topic_end><topic_start>Debugging intrinsic passesTo determine whether you have excessive intrinsic passes,enable the Track layouts optionin DevTools (disabled by default),and look at the app’s stack traceto learn how many layout passes were performed.Once you enable tracking, intrinsic timeline eventsare labeled as ‘$runtimeType intrinsics’.<topic_end><topic_start>Avoiding intrinsic passesYou have a couple options for avoiding the intrinsic pass:To dive even deeper into how layout works,check out the layout and renderingsection in the Flutter architectural overview.<topic_end><topic_start>Build and display frames in 16msSince there are two separate threads for buildingand rendering, you have 16ms for building,and 16ms for rendering on a 60Hz display.If latency is a concern,build and display a frame in 16ms or less.Note that means built in 8ms or less,and rendered in 8ms or less,for a total of 16ms or less.If your frames are rendering in well under16ms total in profile mode,you likely don’t have to worry about performanceeven if some performance pitfalls apply,but you should still aim to build andrender a frame as fast as possible. Why?If you are wondering why 60fps leads to a smooth visual experience,check out the video Why 60fps?<topic_end><topic_start>PitfallsIf you need to tune your app’s performance,or perhaps the UI isn’t as smooth as you expect,the DevTools Performance view can help!Also, the Flutter plugin for your IDE mightbe useful. In the Flutter Performance window,enable the Show widget rebuild information check box.This feature helps you detect when frames arebeing rendered and displayed in more than 16ms.Where possible,the plugin provides a link to a relevant tip.The following behaviors might negatively impactyour app’s performance.Avoid using the Opacity widget,and particularly avoid it in an animation.Use AnimatedOpacity or FadeInImage instead.For more information, check outPerformance considerations for opacity animation.When using an AnimatedBuilder,avoid putting a subtree in the builderfunction that builds widgets that don’tdepend on the animation. This subtree isrebuilt for every tick of the animation.Instead, build that part of the subtreeonce and pass it as a child tothe AnimatedBuilder. For more information,check out Performance optimizations.Avoid clipping in an animation.If possible, pre-clip the image before animating it.Avoid using constructors with a concrete Listof children (such as Column() or ListView())if most of the children are not visibleon screen to avoid the build cost.Avoid overriding operator == on Widget objects.While it might seem like it would help by avoiding unnecessary rebuilds,in practice it hurts performance because it results in O(N²) behavior.The only exception to this rule is leaf widgets (widgets with no children),in the specific case where comparing the properties of the widgetis likely to be significantly more efficient than rebuilding the widgetand where the widget will rarely change configuration.Even in such cases,it is generally preferable to rely on caching the widgets,because even one override of operator ==can result in across-the-board performance degradationas the compiler can no longer assume that the call is always static.<topic_end><topic_start>ResourcesFor more performance info, check out the following resources:<topic_end><topic_start>Measuring your app's sizeMany developers are concerned with the size of their compiled app.As the APK, app bundle, or IPA version of a Flutter app isself-contained and holds all the code and assets needed to run the app,its size can be a concern. The larger an app,the more space it requires on a device,the longer it takes to download,and it might break the limit of usefulfeatures like Android instant apps.<topic_end><topic_start>Debug builds are not representativeBy default, launching your app with flutter run,or by clicking the Play button in your IDE(as used in Test drive andWrite your first Flutter app),generates a debug build of the Flutter app.The app size of a debug build is large due tothe debugging overhead that allows for hot reloadand source-level debugging. As such, it is not representative of a productionapp end users download.<topic_end><topic_start>Checking the total sizeA default release build, such as one created by flutter build apk orflutter build ios, is built to conveniently assemble your upload packageto the Play Store and App Store. As such, they’re also not representative ofyour end-users’ download size. The stores generally reprocess and splityour upload package to target the specific downloader and the downloader’shardware, such as filtering for assets targeting the phone’s DPI, filteringnative libraries targeting the phone’s CPU architecture.<topic_end><topic_start>Estimating total sizeTo get the closest approximate size on each platform, use the followinginstructions.<topic_end><topic_start>AndroidFollow the Google Play Console’s instructions for checking app download andinstall sizes.Produce an upload package for your application:Log into your Google Play Console. Upload your application binary by dragdropping the .aab file.View the application’s download and install size in the Android vitals ->App size tab.The download size is calculated based on an XXXHDPI (~640dpi) device on anarm64-v8a architecture. Your end users’ download sizes might vary depending ontheir hardware.The top tab has a toggle for download size and install size. The page alsocontains optimization tips further below.<topic_end><topic_start>iOSCreate an Xcode App Size Report.First, by configuring the app version and build as described in theiOS create build archive instructions.Then:Sign and export the IPA. The exported directory containsApp Thinning Size Report.txt with details about your projectedapplication size on different devices and versions of iOS.The App Size Report for the default demo app in Flutter 1.17 shows:In this example, the app has an approximatedownload size of 5.4 MB and an approximateinstallation size of 13.7 MB on an iPhone12,1 (Model ID / Hardwarenumber for iPhone 11)and iPhone11,8 (iPhone XR) running iOS 13.0.To measure an iOS app exactly,you have to upload a release IPA to Apple’sApp Store Connect (instructions)and obtain the size report from there.IPAs are commonly larger than APKs as explainedin How big is the Flutter engine?, asection in the Flutter FAQ.<topic_end><topic_start>Breaking down the sizeStarting in Flutter version 1.22 and DevTools version 0.9.1,a size analysis tool is included to help developers understand the breakdownof the release build of their application.warning Warning  As stated in the checking total size section  above, an upload package is not representative of your end users’ download  size. Be aware that redundant native library architectures and asset densities  seen in the breakdown tool can be filtered by the Play Store and App Store.The size analysis tool is invoked by passing the --analyze-size flag whenbuilding:This build is different from a standard release build in two ways.In addition to analyzing a single build, two builds can also be diffed byloading two *-code-size-analysis_*.json files into DevTools. SeeDevTools documentation for details.Through the summary, you can get a quick idea of the size usage per category(such as asset, native code, Flutter libraries, etc). The compiled Dartnative library is further broken down by package for quick analysis.warning Warning  This tool on iOS creates a .app rather than an IPA. Use this tool to  evaluate the relative size of the .app’s content. To get  a closer estimate of the download size, reference the  Estimating total size section above.<topic_end><topic_start>Deeper analysis in DevToolsThe *-code-size-analysis_*.json file produced above can be furtheranalyzed in deeper detail in DevTools where a tree or a treemap view canbreak down the contents of the application into the individual file level andup to function level for the Dart AOT artifact.This can be done by flutter pub global run devtools, selectingOpen app size tool and uploading the JSON file.For further information on using the DevTools app size tool, seeDevTools documentation.<topic_end><topic_start>Reducing app sizeWhen building a release version of your app,consider using the --split-debug-info tag.This tag can dramatically reduce code size.For an example of using this tag, seeObfuscating Dart code.Some other things you can do to make your app smaller are:<topic_end><topic_start>Deferred components<topic_end><topic_start>IntroductionFlutter has the capability to build apps that candownload additional Dart code and assets at runtime.This allows apps to reduce install apk size and downloadfeatures and assets when needed by the user.We refer to each uniquely downloadable bundle of Dartlibraries and assets as a “deferred component”.To load these components, use Dart’s deferred imports.They can be compiled into split AOT and JavaScript shared libraries.info Note  Flutter supports deferred, or “lazy”, loading on Android and the web.  The implementations differ.  Android’s dynamic feature modules deliver the  deferred components packaged as Android modules.  The web creates these components as separate *.js files.  Deferred code doesn’t impact other platforms,  which continue to build as normal with all deferred  components and assets included at initial install time.Though you can defer loading modules,you must build the entire app and upload that app as a singleAndroid App Bundle (*.aab).Flutter doesn’t support dispatching partial updates without re-uploadingnew Android App Bundles for the entire application.Flutter performs deferred loading when you compile your appin release or profile mode.Debug mode treats all deferred components as regular imports.The components are present at launch and load immediately.This allows debug builds to hot reload.For a deeper dive into the technical details ofhow this feature works, see Deferred Componentson the Flutter wiki.<topic_end><topic_start>How to set your project up for deferred componentsThe following instructions explain how to set up yourAndroid app for deferred loading.<topic_end><topic_start>Step 1: Dependencies and initial project setupAdd Play Core to the Android app’s    build.gradle dependencies.    In android/app/build.gradle add the following:If using the Google Play Store as the    distribution model for dynamic features,    the app must support SplitCompat and provide an instance    of a PlayStoreDeferredComponentManager.    Both of these tasks can be accomplished by setting    the android:name property on the application in    android/app/src/main/AndroidManifest.xml to    io.flutter.embedding.android.FlutterPlayStoreSplitApplication:io.flutter.app.FlutterPlayStoreSplitApplication handlesboth of these tasks for you. If you useFlutterPlayStoreSplitApplication,you can skip to step 1.3.If your Android applicationis large or complex, you might want to separately supportSplitCompat and provide thePlayStoreDynamicFeatureManager manually.To support SplitCompat, there are three methods(as detailed in the Android docs), any of which are valid:Make your application class extend    SplitCompatApplication:Call SplitCompat.install(this);    in the attachBaseContext() method:Declare SplitCompatApplication as the application    subclass and add the Flutter compatibility code from  FlutterApplication to your application class:The embedder relies on an injectedDeferredComponentManager instance to handleinstall requests for deferred components.Provide a PlayStoreDeferredComponentManager intothe Flutter embedder by adding the following codeto your app initialization:Opt into deferred components by adding    the deferred-components entry to the app’s pubspec.yaml    under the flutter entry:The flutter tool looks for the deferred-components  entry in the pubspec.yaml to determine whether the  app should be built as deferred or not.  This can be left empty for now unless you already  know the components desired and the Dart deferred libraries  that go into each. You will fill in this section later  in step 3.3 once gen_snapshot produces the loading units.<topic_end><topic_start>Step 2: Implementing deferred Dart librariesNext, implement deferred loaded Dart libraries in yourapp’s Dart code. The implementation does not needto be feature complete yet. The example in therest of this page adds a new simple deferred widgetas a placeholder. You can also convert existing codeto be deferred by modifying the imports andguarding usages of deferred code behind loadLibrary()Futures.Create a new Dart library.    For example, create a new DeferredBox widget that    can be downloaded at runtime.    This widget can be of any complexity but,    for the purposes of this guide,    create a simple box as a stand-in.    To create a simple blue box widget,    create box.dart with the following contents:<code_start>// box.dartimport 'package:flutter/material.dart';/// A simple blue 30x30 box.class DeferredBox extends StatelessWidget {  const DeferredBox({super.key});  @override  Widget build(BuildContext context) {    return Container(      height: 30,      width: 30,      color: Colors.blue,    );  }}<code_end>Import the new Dart library    with the deferred keyword in your app and    call loadLibrary() (see lazily loading a library).    The following example uses FutureBuilder    to wait for the loadLibrary Future (created in    initState) to complete and display a    CircularProgressIndicator as a placeholder.    When the Future completes, it returns the DeferredBox widget.    SomeWidget can then be used in the app as normal and    won’t ever attempt to access the deferred Dart code until    it has successfully loaded.<code_start>import 'package:flutter/material.dart';import 'box.dart' deferred as box;class SomeWidget extends StatefulWidget {  const SomeWidget({super.key});  @override  State<SomeWidget> createState() => _SomeWidgetState();}class _SomeWidgetState extends State<SomeWidget> {  late Future<void> _libraryFuture;  @override  void initState() {    super.initState();    _libraryFuture = box.loadLibrary();  }  @override  Widget build(BuildContext context) {    return FutureBuilder<void>(      future: _libraryFuture,      builder: (context, snapshot) {        if (snapshot.connectionState == ConnectionState.done) {          if (snapshot.hasError) {            return Text('Error: ${snapshot.error}');          }          return box.DeferredBox();        }        return const CircularProgressIndicator();      },    );  }}<code_end>The loadLibrary() function returns a Future<void>that completes successfully when the code in the libraryis available for use and completes with an error otherwise.All usage of symbols from the deferred library should beguarded behind a completed loadLibrary() call. All importsof the library must be marked as deferred for it to becompiled appropriately to be used in a deferred component.If a component has already been loaded, additional callsto loadLibrary() complete quickly (but not synchronously).The loadLibrary() function can also be called early totrigger a pre-load to help mask the loading time.You can find another example of deferred import loading inFlutter Gallery’s lib/deferred_widget.dart.<topic_end><topic_start>Step 3: Building the appUse the following flutter command to build adeferred components app:This command assists you by validating that your projectis properly set up to build deferred components apps.By default, the build fails if the validator detectsany issues and guides you through suggested changes to fix them.info Note  You can opt out of building deferred components  with the --no-deferred-components flag.  This flag causes all assets defined under  deferred components to be treated as if they were  defined under the assets section of pubspec.yaml.  All Dart code is compiled into a single shared library  and loadLibrary() calls complete in the next event  loop boundary (as soon as possible while being asynchronous).  This flag is also equivalent to omitting the deferred-components:  entry in pubspec.yaml.The    flutter build appbundle command    runs the validator and attempts to build the app with    gen_snapshot instructed to produce split AOT shared libraries    as separate .so files. On the first run, the validator will    likely fail as it detects issues; the tool makes    recommendations for how to set up the project and fix these issues.The validator is split into two sections: prebuildand post-gen_snapshot validation. This is because anyvalidation referencing loading units cannot be performeduntil gen_snapshot completes and produces a final setof loading units.info Note  You can opt to have the tool attempt to build your  app without the validator by passing the  --no-validate-deferred-components flag.  This can result in unexpected and confusing  instructions to resolve failures.  This flag is meant to be used in  custom implementations that do not rely on the default  Play-store-based implementation that the validator checks for.The validator detects any new, changed, or removedloading units generated by gen_snapshot.The current generated loading units are tracked in your<projectDirectory>/deferred_components_loading_units.yaml file.This file should be checked into source control to ensure thatchanges to the loading units by other developers can be caught.The validator also checks for the following in theandroid directory:<projectDir>/android/app/src/main/res/values/strings.xml    An entry for every deferred component mapping the key    ${componentName}Name to ${componentName}.    This string resource is used by the AndroidManifest.xml    of each feature module to define the dist:title property.    For example:<projectDir>/android/<componentName>    An Android dynamic feature module for    each deferred component exists and contains a build.gradle    and src/main/AndroidManifest.xml file.    This only checks for existence and does not validate    the contents of these files. If a file does not exist,    it generates a default recommended one.<projectDir>/android/app/src/main/res/values/AndroidManifest.xml    Contains a meta-data entry that encodes    the mapping between loading units and component name the    loading unit is associated with. This mapping is used by the    embedder to convert Dart’s internal loading unit id    to the name of a deferred component to install. For example:The gen_snapshot validator won’t run until the prebuildvalidator passes.For each of these checks,    the tool produces the modified or new files    needed to pass the check.    These files are placed in the    <projectDir>/build/android_deferred_components_setup_files directory.    It is recommended that the changes be applied by    copying and overwriting the same files in the    project’s android directory. Before overwriting,    the current project state should be committed to    source control and the recommended changes should be    reviewed to be appropriate. The tool won’t make any    changes to your android/ directory automatically.Once the available    loading units are generated and logged in    <projectDirectory>/deferred_components_loading_units.yaml,    it is possible to fully configure the pubspec’s    deferred-components section so that the loading units    are assigned to deferred components as desired.    To continue with the box example, the generated    deferred_components_loading_units.yaml file would contain:The loading unit id (‘2’ in this case) is usedinternally by Dart, and can be ignored.The base loading unit (id ‘1’) is not listedand contains everything not explicitly containedin another loading unit.You can now add the following to pubspec.yaml:To assign a loading unit to a deferred component,add any Dart lib in the loading unit into thelibraries section of the feature module.Keep the following guidelines in mind:Loading units should not be included    in more than one component.Including one Dart library from a    loading unit indicates that the entire loading    unit is assigned to the deferred component.All loading units not assigned to    a deferred component are included in the base component,    which always exists implicitly.Loading units assigned to the same    deferred component are downloaded, installed,    and shipped together.The base component is implicit and    need not be defined in the pubspec.Assets can also be included by adding    an assets section in the deferred component configuration:An asset can be included in multiple deferred components,but installing both components results in a replicated asset.Assets-only components can also be defined by omitting thelibraries section. These assets-only components must beinstalled with the DeferredComponent utility class inservices rather than loadLibrary().Since Dart libs are packaged together with assets,if a Dart library is loaded with loadLibrary(),any assets in the component are loaded as well.However, installing by component name and the services utilitywon’t load any dart libraries in the component.You are free to include assets in any component,as long as they are installed and loaded when theyare first referenced, though typically,assets and the Dart code that uses those assetsare best packed in the same component.Manually add all deferred components    that you defined in pubspec.yaml into the    android/settings.gradle file as includes.    For example, if there are three deferred components    defined in the pubspec named, boxComponent, circleComponent,    and assetComponent, ensure that android/settings.gradle    contains the following:Repeat steps 3.1 through 3.6 (this step)    until all validator recommendations are handled and the tool    runs without further recommendations.When successful, this command outputs an app-release.aabfile in build/app/outputs/bundle/release.A successful build does not always mean the app wasbuilt as intended. It is up to you to ensure that all loadingunits and Dart libraries are included in the way you intended.For example, a common mistake is accidentally importing aDart library without the deferred keyword,resulting in a deferred library being compiled as part ofthe base loading unit. In this case, the Dart lib wouldload properly because it is always present in the base,and the lib would not be split off. This can be checkedby examining the deferred_components_loading_units.yamlfile to verify that the generated loading units are describedas intended.When adjusting the deferred components configurations,or making Dart changes that add, modify, or remove loading units,you should expect the validator to fail.Follow steps 3.1 through 3.6 (this step) to apply anyrecommended changes to continue the build.<topic_end><topic_start>Running the app locallyOnce your app has successfully built an .aab file,use Android’s bundletool to performlocal testing with the --local-testing flag.To run the .aab file on a test device,download the bundletool jar executable fromgithub.com/google/bundletool/releases and run:Where <your_app_project_dir> is the path to your app’sproject directory and <your_temp_dir> is any temporarydirectory used to store the outputs of bundletool.This unpacks your .aab file into an .apks file andinstalls it on the device. All available Android dynamicfeatures are loaded onto the device locally andinstallation of deferred components is emulated.Before running build-apks again,remove the existing app .apks file:Changes to the Dart codebase require either incrementingthe Android build ID or uninstalling and reinstallingthe app, as Android won’t update the feature modulesunless it detects a new version number.<topic_end><topic_start>Releasing to the Google Play StoreThe built .aab file can be uploaded directly tothe Play store as normal. When loadLibrary() is called,the needed Android module containing the Dart AOT lib andassets is downloaded by the Flutter engine using thePlay store’s delivery feature.<topic_end><topic_start>Improving rendering performanceinfo Note  To learn how to use the Performance View  (part of Flutter DevTools)  for debugging performance issues,  see Using the Performance view.Rendering animations in your app is one of the most citedtopics of interest when it comes to measuring performance.Thanks in part to Flutter’s Skia engine and its abilityto quickly create and dispose of widgets,Flutter applications are performant by default,so you only need to avoid common pitfalls to achieveexcellent performance.<topic_end><topic_start>General adviceIf you see janky (non-smooth) animations, makesure that you are profiling performance with anapp built in profile mode.The default Flutter build creates an app in debug mode,which is not indicative of release performance.For information,see Flutter’s build modes.A couple common pitfalls:For more information on evaluating performanceincluding information on common pitfalls,see the following docs:<topic_end><topic_start>Mobile-only adviceDo you see noticeable jank on your mobile app, but only onthe first run of an animation? If so, seeReduce shader animation jank on mobile.<topic_end><topic_start>Web-only adviceThe following series of articles cover what the Flutter Materialteam learned when improving performance of the Flutter Galleryapp on the web:<topic_end><topic_start>Flutter performance profilinginfo Note  To learn how to use the Performance View  (part of Flutter DevTools)  for debugging performance issues,  see Using the Performance view.<topic_end><topic_start>What you'll learnIt’s been said that “a fast app is great,but a smooth app is even better.”If your app isn’t rendering smoothly,how do you fix it? Where do you begin?This guide shows you where to start,steps to take, and tools that can help.info Note<topic_end><topic_start>Diagnosing performance problemsTo diagnose an app with performance problems, you’ll enablethe performance overlay to look at the UI and raster threads.Before you begin, make sure that you’re running inprofile mode, and that you’re not using an emulator.For best results, you might choose the slowest device thatyour users might use.<topic_end><topic_start>Connect to a physical deviceAlmost all performance debugging for Flutter applicationsshould be conducted on a physical Android or iOS device,with your Flutter application running in profile mode.Using debug mode, or running apps on simulatorsor emulators, is generally not indicative of the finalbehavior of release mode builds.You should consider checking performanceon the slowest device that your users might reasonably use.<topic_end><topic_start>Why you should run on a real device:<topic_end><topic_start>Run in profile modeFlutter’s profile mode compiles and launches your applicationalmost identically to release mode, but with just enough additionalfunctionality to allow debugging performance problems.For example, profile mode provides tracing information to theprofiling tools.info Note  DevTools can’t connect to a Flutter web app running  in profile mode. Use Chrome DevTools to  generate timeline events for a web app.Launch the app in profile mode as follows:In VS Code, open your launch.json file, and set theflutterMode property to profile(when done profiling, change it back to release or debug):From the command line, use the --profile flag:For more information on the different modes,see Flutter’s build modes.You’ll begin by opening DevTools and viewingthe performance overlay, as discussed in the next section.<topic_end><topic_start>Launch DevToolsDevTools provides features like profiling, examining the heap,displaying code coverage, enabling the performance overlay,and a step-by-step debugger.DevTools’ Timeline view allows you to investigate theUI performance of your application on a frame-by-frame basis.Once your app is running in profile mode,launch DevTools.<topic_end><topic_start>The performance overlayThe performance overlay displays statistics in two graphsthat show where time is being spent in your app. If the UIis janky (skipping frames), these graphs help you figure out why.The graphs display on top of your running app, but they aren’tdrawn like a normal widget—the Flutter engine itselfpaints the overlay and only minimally impacts performance.Each graph represents the last 300 frames for that thread.This section describes how to enable the performance overlayand use it to diagnose the cause of jank in your application.The following screenshot shows the performance overlay runningon the Flutter Gallery example:Performance overlay showing the raster thread (top),and UI thread (bottom).The vertical green barsrepresent the current frame.<topic_end><topic_start>Interpreting the graphsThe top graph (marked “GPU”) shows the time spent by the raster thread, the bottom one graph shows the time spent by the UI thread.The white lines across the graphs show 16ms incrementsalong the vertical axis; if the graph ever goes over oneof these lines then you are running at less than 60Hz.The horizontal axis represents frames. The graph isonly updated when your application paints,so if it’s idle the graph stops moving.The overlay should always be viewed in profile mode,since debug mode performance is intentionally sacrificedin exchange for expensive asserts that are intended to aiddevelopment, and thus the results are misleading.Each frame should be created and displayed within 1/60th ofa second (approximately 16ms). A frame exceeding this limit(in either graph) fails to display, resulting in jank,and a vertical red bar appears in one or both of the graphs.If a red bar appears in the UI graph, the Dart code is tooexpensive. If a red vertical bar appears in the GPU graph,the scene is too complicated to render quickly.The vertical red bars indicate that the current frame isexpensive to both render and paint.When both graphsdisplay red, start by diagnosing the UI thread.<topic_end><topic_start>Flutter’s threadsFlutter uses several threads to do its work, thoughonly two of the threads are shown in the overlay.All of your Dart code runs on the UI thread.Although you have no direct access to any other thread,your actions on the UI thread have performance consequenceson other threads.For links to more information and videos,see The Framework architecture on theGitHub wiki, and the community article,The Layer Cake.<topic_end><topic_start>Displaying the performance overlayYou can toggle display of the performance overlay as follows:<topic_end><topic_start>Using the Flutter inspectorThe easiest way to enable the PerformanceOverlay widget isfrom the Flutter inspector, which is available in theInspector view in DevTools. Simply click thePerformance Overlay button to toggle the overlayon your running app.<topic_end><topic_start>From the command lineToggle the performance overlay using the P key fromthe command line.<topic_end><topic_start>ProgrammaticallyTo enable the overlay programmatically, seePerformance overlay, a section in theDebugging Flutter apps programmatically page.<topic_end><topic_start>Identifying problems in the UI graphIf the performance overlay shows red in the UI graph,start by profiling the Dart VM, even if the GPU graphalso shows red.<topic_end><topic_start>Identifying problems in the GPU graphSometimes a scene results in a layer tree that is easy to construct,but expensive to render on the raster thread. When this happens,the UI graph has no red, but the GPU graph shows red.In this case, you’ll need to figure out what your code is doingthat is causing rendering code to be slow. Specific kinds of workloadsare more difficult for the GPU. They might involve unnecessary callsto saveLayer, intersecting opacities with multiple objects,and clips or shadows in specific situations.If you suspect that the source of the slowness is during an animation,click the Slow Animations button in the Flutter inspectorto slow animations down by 5x.If you want more control on the speed, you can also do thisprogrammatically.Is the slowness on the first frame, or on the whole animation?If it’s the whole animation, is clipping causing the slow down?Maybe there’s an alternative way of drawing the scene that doesn’tuse clipping. For example, overlay opaque corners onto a squareinstead of clipping to a rounded rectangle.If it’s a static scene that’s being faded, rotated, or otherwisemanipulated, a RepaintBoundary might help.<topic_end><topic_start>Checking for offscreen layersThe saveLayer method is one of the most expensive methods inthe Flutter framework. It’s useful when applying post-processingto the scene, but it can slow your app and should be avoided ifyou don’t need it.  Even if you don’t call saveLayer explicitly,implicit calls might happen on your behalf. You can check whetheryour scene is using saveLayer with thePerformanceOverlayLayer.checkerboardOffscreenLayers switch.Once the switch is enabled, run the app and look for any imagesthat are outlined with a flickering box. The box flickers fromframe to frame if a new frame is being rendered.  For example,perhaps you have a group of objects with opacities that are renderedusing saveLayer. In this case, it’s probably more performant toapply an opacity to each individual widget, rather than a parentwidget higher up in the widget tree. The same goes forother potentially expensive operations, such as clipping or shadows.info Note  Opacity, clipping, and shadows are not, in themselves,  a bad idea. However, applying them to the top of the  widget tree might cause extra calls to saveLayer,  and needless processing.When you encounter calls to saveLayer,ask yourself these questions:<topic_end><topic_start>Checking for non-cached imagesCaching an image with RepaintBoundary is good,when it makes sense.One of the most expensive operations,from a resource perspective,is rendering a texture using an image file.First, the compressed imageis fetched from persistent storage.The image is decompressed into host memory (GPU memory),and transferred to device memory (RAM).In other words, image I/O can be expensive.The cache provides snapshots of complex hierarchies sothey are easier to render in subsequent frames.Because raster cache entries are expensive toconstruct and take up loads of GPU memory,cache images only where absolutely necessary.You can see which images are being cached by enabling thePerformanceOverlayLayer.checkerboardRasterCacheImages switch.Run the app and look for images rendered with a randomly coloredcheckerboard, indicating that the image is cached.As you interact with the scene, the checkerboarded imagesshould remain constant—you don’t want to see flickering,which would indicate that the cached image is being re-cached.In most cases, you want to see checkerboards on static images,but not on non-static images.  If a static image isn’t cached,you can cache it by placing it into a RepaintBoundarywidget. Though the engine might still ignore a repaintboundary if it thinks the image isn’t complex enough.<topic_end><topic_start>Viewing the widget rebuild profilerThe Flutter framework is designed to make it hard to createapplications that are not 60fps and smooth. Often, if you have jank,it’s because there is a simple bug causing more of the UI to berebuilt each frame than required. The Widget rebuild profilerhelps you debug and fix performance problems due to these sortsof bugs.You can view the widget rebuilt counts for the current screen andframe in the Flutter plugin for Android Studio and IntelliJ.For details on how to do this, see Show performance data<topic_end><topic_start>BenchmarkingYou can measure and track your app’s performance by writingbenchmark tests. The Flutter Driver library provides supportfor benchmarking. Using this integration test framework,you can generate metrics to track the following:Tracking these benchmarks allows you to be informed when aregression is introduced that adversely affects performance.For more information, check out Integration testing.<topic_end><topic_start>Other resourcesThe following resources provide more information on usingFlutter’s tools and debugging in Flutter:<topic_end><topic_start>Debugging performance for web appsinfo Note  Profiling Flutter web apps requires Flutter version 3.14 or later.The Flutter framework emits timeline events as it works to build frames,draw scenes, and track other activity such as garbage collections.These events are exposed in the Chrome DevTools performance panel for debugging.You can also emit your own timeline events using the dart:developerTimeline and TimelineTask APIs for further performance analysis.<topic_end><topic_start>Optional flags to enhance tracingTo configure which timeline events are tracked, set any of the following top-level properties to truein your app’s main method.<topic_end><topic_start>Instructions<topic_end><topic_start>Shader compilation jankinfo Note  To learn how to use the Performance View  (part of Flutter DevTools)  for debugging performance issues,  see Using the Performance view.If the animations on your mobile app appear to be janky,but only on the first run,this is likely due to shader compilation.Flutter’s long term solution toshader compilation jank is Impeller,which is in the stable release for iOSand in preview behind a flag on Android.While we work on making Impeller fully production ready,you can mitigate shader compilation jank by bundlingprecompiled shaders with an iOS app.Unfortunately, this approach doesn’t work well on Androiddue to precompiled shaders being device or GPU-specific.The Android hardware ecosystem is large enough that theGPU-specific precompiled shaders bundled with an applicationwill work on only a small subset of devices,and will likely make jank worse on the other devices,or even create rendering errors.Also, note that we aren’t planning to makeimprovements to the developer experience for creatingprecompiled shaders described below. Instead,we are focusing our energies on the more robustsolution to this problem that Impeller offers.<topic_end><topic_start>What is shader compilation jank?A shader is a piece of code that runs on aGPU (graphics processing unit).When the Skia graphics backend that Flutter uses for renderingsees a new sequence of draw commands for the first time,it sometimes generates and compiles acustom GPU shader for that sequence of commands.This allows that sequence and potentially similar sequencesto render as fast as possible.Unfortunately, Skia’s shader generation and compilationhappens in sequence with the frame workload.The compilation could cost up to a few hundred millisecondswhereas a smooth frame needs to be drawn within 16 millisecondsfor a 60 fps (frame-per-second) display.Therefore, a compilation could cause tens of framesto be missed, and drop the fps from 60 to 6.This is compilation jank.After the compilation is complete,the animation should be smooth.On the other hand, Impeller generates and compiles allnecessary shaders when we build the Flutter Engine.Therefore apps running on Impeller already haveall the shaders they need, and the shaders can be usedwithout introducing jank into animations.Definitive evidence for the presence of shader compilation jankis to set GrGLProgramBuilder::finalize in the tracingwith --trace-skia enabled.The following screenshot shows an example timeline tracing.<topic_end><topic_start>What do we mean by “first run”?On iOS, “first run” means that the user might seejank when an animation first occurs every timethe user opens the app from scratch.<topic_end><topic_start>How to use SkSL warmupFlutter provides command line toolsfor app developers to collect shaders that might be neededfor end-users in the SkSL (Skia Shader Language) format.The SkSL shaders can then be packaged into the app,and get warmed up (pre-compiled) when an end-user firstopens the app, thereby reducing the compilationjank in later animations.Use the following instructions to collectand package the SkSL shaders:Run the app with --cache-sksl turned on    to capture shaders in SkSL:If the same app has been previously runwithout --cache-sksl, then the--purge-persistent-cache flag might be needed:This flag removes older non-SkSL shader caches thatcould interfere with SkSL shader capturing.It also purges the SkSL shaders so use it only on the first--cache-sksl run.Play with the app to trigger as many animations    as needed; particularly those with compilation jank.Press M at the command line of flutter run to    write the captured SkSL shaders into a file named something like   flutter_01.sksl.json.   For best results,   capture SkSL shaders on an actual iOS device.   A shader captured on a simulator isn’t likely to work correctly   on actual hardware.Build the app with SkSL warm-up using the following,    as appropriate:If it’s built for a driver test like test_driver/app.dart,make sure to also specify --target=test_driver/app.dart(for example, flutter build ios --bundle-sksl-pathflutter_01.sksl.json --target=test_driver/app.dart).Test the newly built app.Alternatively, you can write some integration tests toautomate the first three steps using a single command.For example:With such integration tests,you can easily and reliably get thenew SkSLs when the app code changes,or when Flutter upgrades.Such tests can also be used to verify the performance changebefore and after the SkSL warm-up.Even better, you can put those tests into aCI (continuous integration) system so theSkSLs are generated and tested automatically over the lifetime of an app.info Note  The integration_test package is now the recommended way  to write integration tests. Refer to the  Integration testing  page for details.Take the original version of Flutter Gallery as an example.The CI system is set up to generate SkSLs for every Flutter commit,and verifies the performance, in the transitions_perf_test.dart test.For more details,check out the flutter_gallery_sksl_warmup__transition_perf andflutter_gallery_sksl_warmup__transition_perf_e2e_ios32 tasks.The worst frame rasterization time is a useful metric fromsuch integration tests to indicate the severity of shadercompilation jank.For instance,the steps above reduce Flutter gallery’s shader compilationjank and speeds up its worst frame rasterization time on aMoto G4 from ~90 ms to ~40 ms. On iPhone 4s,it’s reduced from ~300 ms to ~80 ms. That leads to the visualdifference as illustrated in the beginning of this article.<topic_end><topic_start>Performance metricsFor a complete list of performance metrics Flutter measures per commit, visit the following sites, click Query, and filter the test and sub_result fields:<topic_end><topic_start>Concurrency and isolatesAll Dart code runs in isolates,which are similar to threads,but differ in that isolates have their own isolated memory.They do not share state in any way,and can only communicate by messaging.By default,Flutter apps do all of their work on a single isolate –the main isolate.In most cases, this model allows for simpler programming andis fast enough that the application’s UI doesn’t become unresponsive.Sometimes though,applications need to perform exceptionally large computationsthat can cause “UI jank” (jerky motion).If your app is experiencing jank for this reason,you can move these computations to a helper isolate.This allows the underlying runtime environmentto run the computation concurrentlywith the main UI isolate’s workand takes advantage of multi-core devices.Each isolate has its own memoryand its own event loop.The event loop processesevents in the order that they’re added to an event queue.On the main isolate,these events can be anything from handling a user tapping in the UI,to executing a function,to painting a frame on the screen.The following figure shows an example event queuewith 3 events waiting to be processed.For smooth rendering,Flutter adds a “paint frame” event to the event queue60 times per second(for a 60Hz device).If these events aren’t processed on time,the application experiences UI jank,or worse,become unresponsive altogether.Whenever a process can’t be completed in a frame gap,the time between two frames,it’s a good idea to offload the work to another isolateto ensure that the main isolate can produce 60 frames per second.When you spawn an isolate in Dart,it can process the work concurrently with the main isolate,without blocking it.You can read more about how isolatesand the event loop work in Dart onthe concurrency page of the Dartdocumentation.<topic_end><topic_start>Common use cases for isolatesThere is only one hard rule for when you should use isolates,and that’s when large computations are causing your Flutter applicationto experience UI jank.This jank happens when there is any computation that takes longer thanFlutter’s frame gap.Any process could take longer to complete,depending on the implementationand the input data,making it impossible to create an exhaustive list ofwhen you need to consider using isolates.That said, isolates are commonly used for the following:<topic_end><topic_start>Message passing between isolatesDart’s isolates are an implementation of the Actor model.They can only communicate with each other by message passing,which is done with Port objects.When messages are “passed” between each other,they are generally copied from the sending isolate to thereceiving isolate.This means that any value passed to an isolate,even if mutated on that isolate,doesn’t change the value on the original isolate.The only objects that aren’t copied when passed to an isolateare immutable objects that can’t be changed anyway,such a String or an unmodifiable byte.When you pass an immutable object between isolates,a reference to that object is sent across the port,rather than the object being copied,for better performance.Because immutable objects can’t be updated,this effectively retains the actor model behavior.An exception to this rule iswhen an isolate exits when it sends a message using the Isolate.exit method.Because the sending isolate won’t exist after sending the message,it can pass ownership of the message from one isolate to the other,ensuring that only one isolate can access the message.The two lowest-level primitives that send messages are SendPort.send,which makes a copy of a mutable message as it sends,and Isolate.exit,which sends the reference to the message.Both Isolate.run and computeuse Isolate.exit under the hood.<topic_end><topic_start>Short-lived isolatesThe easiest way to move a process to an isolate in Flutter is withthe Isolate.run method.This method spawns an isolate,passes a callback to the spawned isolate to start some computation,returns a value from the computation,and then shuts the isolate down when the computation is complete.This all happens concurrently with the main isolate,and doesn’t block it.The Isolate.run method requires a single argument,a callback function,that is run on the new isolate.This callback’s function signature must have exactlyone required, unnamed argument.When the computation completes,it returns the callback’s value back to the main isolate,and exits the spawned isolate.For example,consider this code that loads a large JSON blob from a file,and converts that JSON into custom Dart objects.If the json decoding process wasn’t off loaded to a new isolate,this method would cause the UI tobecome unresponsive for several seconds.<code_start>// Produces a list of 211,640 photo objects.// (The JSON file is ~20MB.)Future<List<Photo>> getPhotos() async {  final String jsonString = await rootBundle.loadString('assets/photos.json');  final List<Photo> photos = await Isolate.run<List<Photo>>(() {    final List<Object?> photoData = jsonDecode(jsonString) as List<Object?>;    return photoData.cast<Map<String, Object?>>().map(Photo.fromJson).toList();  });  return photos;}<code_end>For a complete walkthrough of using Isolates toparse JSON in the background, see this cookbook recipe.<topic_end><topic_start>Stateful, longer-lived isolatesShort-live isolates are convenient to use,but there is performance overhead required to spawn new isolates,and to copy objects from one isolate to another.If you’re doing the same computation using Isolate.run repeatedly,you might have better performance by creating isolates that don’t exit immediately.To do this, you can use a handful of lower-level isolate-related APIs thatIsolate.run abstracts:When you use the Isolate.run method,the new isolate immediately shuts down after itreturns a single message to the main isolate.Sometimes, you’ll need isolates that are long lived,and can pass multiple messages to each other over time.In Dart, you can accomplish this with the Isolate APIand Ports.These long-lived isolates are colloquially known as background workers.Long-lived isolates are useful when you have a specific process that eitherneeds to be run repeatedly throughout the lifetime of your application,or if you have a process that runs over a period of timeand needs to yield multiple return values to the main isolate.Or, you might use worker_manager to manage long-lived isolates.<topic_end><topic_start>ReceivePorts and SendPortsSet up long-lived communication between isolates with two classes(in addition to Isolate):ReceivePort and SendPort.These ports are the only way isolates can communicate with each other.Ports behave similarly to Streams,in which the StreamControlleror Sink is created in one isolate,and the listener is set up in the other isolate.In this analogy,the StreamConroller is called a SendPort,and you can “add” messages with the send() method.ReceivePorts are the listeners,and when these listeners receive a new message,they call a provided callback with the message as an argument.For an in-depth explanation on setting up two-waycommunication between the main isolateand a worker isolate,follow the examples in the Dart documentation.<topic_end><topic_start>Using platform plugins in isolatesAs of Flutter 3.7, you can use platform plugins in background isolates.This opens many possibilities to offload heavy,platform-dependent computations to an isolate that won’t block your UI.For example, imagine you’re encrypting datausing a native host API(such as an Android API on Android, an iOS API on iOS, and so on).Previously, marshaling data to the host platform could waste UI thread time,and can now be done in a background isolate.Platform channel isolates use the BackgroundIsolateBinaryMessenger API.The following snippet shows an example of usingthe shared_preferences package in a background isolate.<code_start>import 'dart:isolate';import 'package:flutter/services.dart';import 'package:shared_preferences/shared_preferences.dart';void main() {  // Identify the root isolate to pass to the background isolate.  RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;  Isolate.spawn(_isolateMain, rootIsolateToken);}Future<void> _isolateMain(RootIsolateToken rootIsolateToken) async {  // Register the background isolate with the root isolate.  BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);  // You can now use the shared_preferences plugin.  SharedPreferences sharedPreferences = await SharedPreferences.getInstance();  print(sharedPreferences.getBool('isDebug'));}<code_end><topic_end><topic_start>Limitations of IsolatesIf you’re coming to Dart from a language with multithreading,it’s reasonable to expect isolates to behave like threads,but that isn’t the case.Isolates have their own global fields,and can only communicate with message passing,ensuring that mutable objects in an isolate are only ever accessiblein a single isolate.Therefore, isolates are limited by their access to their own memory.For example,if you have an application with a global mutable variable called configuration,it is copied as a new global field in a spawned isolate.If you mutate that variable in the spawned isolate,it remains untouched in the main isolate.This is true even if you pass the configuration object as a messageto the new isolate.This is how isolates are meant to function,and it’s important to keep in mind when you consider using isolates.<topic_end><topic_start>Web platforms and computeDart web platforms, including Flutter web,don’t support isolates.If you’re targeting the web with your Flutter app,you can use the compute method to ensure your code compiles.The compute() method runs the computation onthe main thread on the web,but spawns a new thread on mobile devices.On mobile and desktop platformsawait compute(fun, message)is equivalent to await Isolate.run(() => fun(message)).For more information on concurrency on the web,check out the concurrency documentation on dart.dev.<topic_end><topic_start>No rootBundle access or dart:ui methodsAll UI tasks and Flutter itself are coupled to the main isolate.Therefore,you can’t access assets using rootBundle in spawned isolates,nor can you perform any widgetor UI work in spawned isolates.<topic_end><topic_start>Limited plugin messages from host platform to FlutterWith background isolate platform channels,you can use platform channels in isolates to send messages to the host platform(for example Android or iOS),and receive responses to those messages.However, you can’t receive unsolicited messages from the host platform.As an example,you can’t set up a long-lived Firestore listener in a background isolate,because Firestore uses platform channels to push updates to Flutter,which are unsolicited.You can, however, query Firestore for a response in the background.<topic_end><topic_start>More informationFor more information on isolates, check out the following resources:<topic_end><topic_start>Performance FAQThis page collects some frequently asked questionsabout evaluating and debugging Flutter’s performance.<topic_end><topic_start>More thoughts about performance<topic_end><topic_start>What is performance?Performance is a set of quantifiable properties of a performer.In this context, performance isn’t the execution of an action itself;it’s how well something or someone performs. Therefore, we use the adjective performant.While the how well part can, in general, be described in natural languages,in our limited scope, the focus is on something that is quantifiable as a realnumber. Real numbers include integers and 0/1 binaries as special cases.Natural language descriptions are still very important. For example, a newsarticle that heavily criticizes Flutter’s performance by just using wordswithout any numbers (a quantifiable value) could still be meaningful, and itcould have great impacts. The limited scope is chosen only because of ourlimited resources.The required quantity to describe performance is often referred to as ametric.To navigate through countless performance issues and metrics, you can categorizebased on performers.For example, most of the content on this website is about the Flutter appperformance, where the performer is a Flutter app. Infra performance is alsoimportant to Flutter, where the performers are build bots and CI task runners:they heavily affect how fast Flutter can incorporate code changes, to improvethe app’s performance.Here, the scope was intentionally broadened to include performance issues otherthan just app performance issues because they can share many tools regardless ofwho the performers are. For example, Flutter app performance and infraperformance might share the same dashboard and similar alert mechanisms.Broadening the scope also allows performers to be included that traditionallyare easy to ignore. Document performance is such an example. The performercould be an API doc of the SDK, and a metric could be: the percentage of readerswho find the API doc useful.<topic_end><topic_start>Why is performance important?Answering this question is not only crucial for validating the work inperformance, but also for guiding the performance work in order to be moreuseful. The answer to “why is performance important?” often is also the answerto “how is performance useful?”Simply speaking, performance is important and useful because, in the scope,performance must have quantifiable properties or metrics. This implies:Not that non-performance, or non-measurable issues or descriptions are notimportant. They’re meant to highlight the scenarios where performance can bemore useful.<topic_end><topic_start>1. A performance report is easy to consumePerformance metrics are numbers. Reading a number is much easier than reading apassage. For example, it probably takes an engineer 1 second to consume theperformance rating as a number from 1 to 5. It probably takes the same engineerat least 1 minute to read the full, 500-word feedback summary.If there are many numbers, it’s easy to summarize or visualize them for quickconsumption. For example, you can quickly consume millions of numbers bylooking at its histogram, average, quantiles, and so on. If a metric has ahistory of thousands of data points, then you can easily plot a timeline toread its trend.On the other hand, having n number of 500-word texts almost guarantees ann-time cost to consume those texts. It would be a daunting task to analyzethousands of historical descriptions, each having 500 words.<topic_end><topic_start>2. Performance has little ambiguityAnother advantage of having performance as a set of numbers is its unambiguity.When you want an animation to have a performance of 20 ms per frame or50 fps, there’s little room for different interpretations about the numbers. Onthe other hand, to describe the same animation in words, someone might call itgood, while someone else might complain that it’s bad. Similarly, the sameword or phrase could be interpreted differently by different people. You mightinterpret an OK frame rate to be 60 fps, while someone else might interpret itto be 30 fps.Numbers can still be noisy. For example, the measured time per frame mightbe a true computation time of this frame, plus a random amount of time (noise)that CPU/GPU spends on some unrelated work. Hence, the metric fluctuates.Nevertheless, there’s no ambiguity of what the number means. And, there arealso rigorous theory and testing tools to handle such noise. For example, youcould take multiple measurements to estimate the distribution of a randomvariable, or you could take the average of many measurements to eliminate thenoise by the law of large numbers.<topic_end><topic_start>3. Performance is comparable and convertiblePerformance numbers not only have unambiguous meanings, but they also haveunambiguous comparisons. For example, there’s no doubt that 5 is greater than 4.On the other hand, it might be subjective to figure out whether excellent isbetter or worse than superb. Similarly, could you figure out whether epic isbetter than legendary? Actually, the phrase strongly exceeds expectationscould be better than superb in someone’s interpretation. It only becomesunambiguous and comparable after a definition that maps strongly exceedsexpectations to 4 and superb to 5.Numbers are also easily convertible using formulas and functions. For example,60 fps can be converted to 16.67 ms per frame. A frame’s renderingtime x (ms) can be converted to a binary indicatorisSmooth = [x <= 16] = (x <= 16 ? 1 :0). Such conversion can be compounded orchained, so you can get a large variety of quantities using a singlemeasurement without any added noise or ambiguity. The converted quantity canthen be used for further comparisons and consumption. Such conversions arealmost impossible if you’re dealing with natural languages.<topic_end><topic_start>4. Performance is fairIf issues rely on verbose words to be discovered, then an unfair advantage isgiven to people who are more verbose (more willing to chat or write) or thosewho are closer to the development team, who have a larger bandwidth and lowercost for chatting or face-to-face meetings.By having the same metrics to detect problems no matter how far away or howsilent the users are, we can treat all issues fairly. That, in turn,allows us to focus on the right issues that have greater impact.<topic_end><topic_start>How to make performance usefulThe following summarizes the 4 points discussed here, from a slightly differentperspective:Make performance metrics easy to consume. Do not overwhelm the readers with alot of numbers (or words). If there are many numbers, then try to summarizethem into a smaller set of numbers (for example, summarize many numbers intoa single average number). Only notify readers when the numbers changesignificantly (for example, automatic alerts on spikes or regressions).Make performance metrics as unambiguous as possible. Define the unit that thenumber is using. Precisely describe how the number is measured. Make thenumber easily reproducible. When there’s a lot of noise, try to show the fulldistribution, or eliminate the noise as much as possible by aggregating manynoisy measurements.Make it easy to compare performance. For example, provide a timeline tocompare the current version with the old version. Provide ways and tools toconvert one metric to another. For example, if we can convert both memoryincrease and fps drops into the number of users dropped or revenue lost indollars, then we can compare them and make an informed trade-off.Make performance metrics monitor a population that is as wide as possible,so no one is left behind.<topic_end><topic_start>Obfuscate Dart code<topic_end><topic_start>What is code obfuscation?Code obfuscation is the process of modifying anapp’s binary to make it harder for humans to understand.Obfuscation hides function and class names in yourcompiled Dart code, replacing each symbol withanother symbol, making it difficult for an attackerto reverse engineer your proprietary app.Flutter’s code obfuscation worksonly on a release build.<topic_end><topic_start>LimitationsNote that obfuscating your code does notencrypt resources nor does it protect againstreverse engineering.It only renames symbols with more obscure names.info  It is a poor security practice to  store secrets in an app.<topic_end><topic_start>Supported targetsThe following build targetssupport the obfuscation processdescribed on this page:info  Web apps don’t support obfuscation.  A web app can be minified, which provides a similar result.  When you build a release version of a Flutter web app,  the web compiler minifies the app. To learn more,  see Build and release a web app.<topic_end><topic_start>Obfuscate your appTo obfuscate your app, use the flutter build commandin release modewith the --obfuscate and  --split-debug-info options.The --split-debug-info option specifies the directorywhere Flutter outputs debug files.In the case of obfuscation, it outputs a symbol map.For example:Once you’ve obfuscated your binary, savethe symbols file. You need this if you laterwant to de-obfuscate a stack trace.lightbulb Tip  The --split-debug-info option can also be used without --obfuscate  to extract Dart program symbols, reducing code size.  To learn more about app size, see Measuring your app’s size.For detailed information on these flags, runthe help command for your specific target, for example:If these flags are not listed in the output,run flutter --version to check your version of Flutter.<topic_end><topic_start>Read an obfuscated stack traceTo debug a stack trace created by an obfuscated app,use the following steps to make it human readable:Find the matching symbols file.For example, a crash from an Android arm64device would need app.android-arm64.symbols.Provide both the stack trace (stored in a file)and the symbols file to the flutter symbolize command.For example:For more information on the symbolize command,run flutter symbolize -h.<topic_end><topic_start>Read an obfuscated nameTo make the name that an app obfuscated human readable,use the following steps:To save the name obfuscation map at app build time,use --extra-gen-snapshot-options=--save-obfuscation-map=/<your-path>.For example:To recover the name, use the generated obfuscation map.The obfuscation map is a flat JSON array with pairs oforiginal names and obfuscated names. For example,["MaterialApp", "ex", "Scaffold", "ey"], where exis the obfuscated name of MaterialApp.<topic_end><topic_start>CaveatBe aware of the following when coding an app that willeventually be an obfuscated binary.<code_start>expect(foo.runtimeType.toString(), equals('Foo'));<code_end><topic_end><topic_start>Create flavors of a Flutter app<topic_end><topic_start>What are flavorsHave you ever wondered how to set up different environments in your Flutter app?Flavors (known as build configurations in iOS and macOS), allow you (the developer) tocreate separate environments for your app using the same code base.For example, you might have one flavor for your full-fledged production app,another as a limited “free” app, another for testing experimental features, and so on.Say you want to make both free and paid versions of your Flutter app.You can use flavors to set up both app versionswithout writing two separate apps.For example, the free version of the app has basic functionality and ads.In contrast, the paid version has basic app functionality, extra features,different styles for paid users, and no ads.You also might use flavors for feature development.If you’ve built a new feature and want to try it out,you could set up a flavor to test it out.Your production code remains unaffecteduntil you’re ready to deploy your new feature.Flavors let you define compile-time configurationsand set parameters that are read at runtime to customizeyour app’s behavior.This document guides you through setting up Flutter flavors for iOS, macOS, and Android.<topic_end><topic_start>Environment set upPrerequisites:To set up flavors in iOS and macOS, you’ll define build configurations in Xcode.<topic_end><topic_start>Creating flavors in iOS and macOSOpen your project in Xcode.Select Product > Scheme > New Scheme from the menu toadd a new Scheme.Duplicate the build configurations to differentiate between thedefault configurations that are already available and the new configurationsfor the free scheme.info Note  Your configurations should be based on your Debug.xconfig or Release.xcconfig  file, not the Pods-Runner.xcconfigs. You can check this by expanding the configuration names.To match the free flavor, add -freeat the end of each new configuration name.Change the free scheme to match the build configurations already created.<topic_end><topic_start>Using flavors in iOS and macOSNow that you’ve set up your free flavor,you can, for example, add different product bundle identifiers per flavor.A bundle identifier uniquely identifies your application.In this example, we set the Debug-free value to equalcom.flavor-test.free.Change the app bundle identifier to differentiate between schemes.In Product Bundle Identifier, append .free to each -free scheme value.In the Build Settings, set the Product Name value to match each flavor.For example, add Debug Free.Add the display name to Info.plist. Update the Bundle Display Namevalue to $(PRODUCT_NAME).Now you have set up your flavor by making a free schemein Xcode and setting the build configurations for that scheme.For more information, skip to the Launching your app flavorssection at the end of this document.<topic_end><topic_start>Plugin configurationsIf your app uses a Flutter plugin, you need to updateios/Podfile (if developing for iOS) and macos/Podfile (if developing for macOS).<topic_end><topic_start>Using flavors in AndroidSetting up flavors in Android can be done in your project’sbuild.gradle file.Inside your Flutter project,navigate to android/app/build.gradle.Create a flavorDimension to group your added product flavors.Gradle doesn’t combine product flavors that share the same dimension.Add a productFlavors object with the desired flavors alongwith values for dimension, resValue,and applicationId or applicationIdSuffix.<topic_end><topic_start>Setting up launch configurationsNext, add a launch.json file; this allows you to run the commandflutter run --flavor [environment name].In VSCode, set up the launch configurations as follows:You can now run the terminal commandflutter run --flavor free or you can set up a runconfiguration in your IDE.<topic_end><topic_start>Launching your app flavorsFor examples of build flavors for iOS, macOS, and Android,check out the integration test samples in the Flutter repo.<topic_end><topic_start>Retrieving your app’s flavor at runtimeFrom your Dart code, you can use the appFlavor API to determine whatflavor your app was built with.<topic_end><topic_start>Conditionally bundling assets based on flavorIf you aren’t familiar with how to add assets to your app, seeAdding assets and images.If you have assets that are only used in a specific flavor in your app, you canconfigure them to only be bundled into your app when building for that flavor.This prevents your app bundle size from being bloated by unused assets.Here is an example:In this example, files within the assets/common/ directory will always be bundledwhen app is built during flutter run or flutter build. Files within theassets/free/ directory are bundled only when the --flavor option is setto free. Similarly, files within the assets/premium directory arebundled only if --flavor is set to premium.<topic_end><topic_start>More informationFor more information on creating and using flavors, check outthe following resources:<topic_end><topic_start>PackagesFor packages that support creating flavors, check out the following:<topic_end><topic_start>Build and release an Android appDuring a typical development cycle,you test an app using flutter run at the command line,or by using the Run and Debugoptions in your IDE. By default,Flutter builds a debug version of your app.When you’re ready to prepare a release version of your app,for example to publish to the Google Play Store,this page can help. Before publishing,you might want to put some finishing touches on your app.This page covers the following topics:info Note   Throughout this page, [project] refers to    the directory that your application is in. While following   these instructions, substitute [project] with    your app’s directory.<topic_end><topic_start>Adding a launcher iconWhen a new Flutter app is created, it has a default launcher icon.To customize this icon, you might want to check out theflutter_launcher_icons package.Alternatively, you can do it manually using the following steps:Review the Material Design producticons guidelines for icon design.In the [project]/android/app/src/main/res/ directory,place your icon files in folders named usingconfiguration qualifiers.The default mipmap- folders demonstrate the correctnaming convention.In AndroidManifest.xml, update theapplication tag’s android:iconattribute to reference icons from the previousstep (for example,<application android:icon="@mipmap/ic_launcher" ...).To verify that the icon has been replaced,run your app and inspect the app icon in the Launcher.<topic_end><topic_start>Enabling Material ComponentsIf your app uses Platform Views, you might want to enableMaterial Components by following the steps described in theGetting Started guide for Android.For example:To find out the latest version, visit Google Maven.<topic_end><topic_start>Sign the appTo publish on the Play Store, you need tosign your app with a digital certificate.Android uses two signing keys: upload and app signing.To create your app signing key, use Play App Signingas described in the official Play Store documentation.To sign your app, use the following instructions.<topic_end><topic_start>Create an upload keystoreIf you have an existing keystore, skip to the next step.If not, create one using one of the following methods:Run the following command at the command line:On macOS or Linux, use the following command:On Windows, use the following command in PowerShell:This command stores the upload-keystore.jks file in your homedirectory. If you want to store it elsewhere, changethe argument you pass to the -keystore parameter.However, keep the keystore file private;don’t check it into public source control!info NoteThe keytool command might not be in your path—it’spart of Java, which is installed as part ofAndroid Studio.  For the concrete path,run flutter doctor -v and locate the path printed after‘Java binary at:’. Then use that fully qualified pathreplacing java (at the end) with keytool.If your path includes space-separated names,such as Program Files, use platform-appropriatenotation for the names. For example, on Mac/Linuxuse Program\ Files, and on Windows use"Program Files".The -storetype JKS tag is only required for Java 9or newer. As of the Java 9 release,the keystore type defaults to PKS12.<topic_end><topic_start>Reference the keystore from the appCreate a file named [project]/android/key.propertiesthat contains a reference to your keystore.Don’t include the angle brackets (< >).They indicate that the text serves as a placeholder for your values.The storeFile might be located at/Users/<user name>/upload-keystore.jks on macOSor C:\\Users\\<user name>\\upload-keystore.jks on Windows.warning Warning  Keep the key.properties file private;  don’t check it into public source control.<topic_end><topic_start>Configure signing in gradleConfigure gradle to use your upload key when building your app in release mode by editing the [project]/android/app/build.gradle file.Add the keystore information from your properties file before the android block:Load the key.properties file into the keystoreProperties object.Find the buildTypes block:And replace it with the following signing configuration info:Release builds of your app will now be signed automatically.info Note  You might need to run flutter clean after changing the gradle file.  This prevents cached builds from affecting the signing process.For more information on signing your app, check outSign your app on developer.android.com.<topic_end><topic_start>Shrinking your code with R8R8 is the new code shrinker from Google, and it’s enabled by defaultwhen you build a release APK or AAB. To disable R8, pass the --no-shrinkflag to flutter build apk or flutter build appbundle.info Note  Obfuscation and minification can considerably extend compile time  of the Android application.<topic_end><topic_start>Enabling multidex supportWhen writing large apps or making use of large plugins,you might encounter Android’s dex limit of 64k methodswhen targeting a minimum API of 20 or below.This might also be encountered when running debug versions of your appusing flutter run that does not have shrinking enabled.Flutter tool supports easily enabling multidex. The simplest way is toopt into multidex support when prompted. The tool detects multidex build errorsand asks before making changes to your Android project.Opting in allows Flutter to automatically depend onandroidx.multidex:multidex and use a generatedFlutterMultiDexApplication as the project’s application.When you try to build and run your app with the Run and Debugoptions in your IDE, your build might fail with the following message:To enable multidex from the command line,run flutter run --debug and select an Android device:When prompted, enter y.The Flutter tool enables multidex support and retries the build:info Note  Multidex support is natively included when targeting  Android SDK 21 or later. However, we don’t recommend  targeting API 21+ purely to resolve the multidex issue  as this might inadvertently exclude users running older devices.You might also choose to manually support multidex by following Android’s guidesand modifying your project’s Android directory configuration.A multidex keep file must be specified to include:Also, include any other classes used in app startup.For more detailed guidance on adding multidex support manually,check out the official Android documentation.<topic_end><topic_start>Reviewing the app manifestReview the default App Manifest file, AndroidManifest.xml.This file is located in [project]/android/app/src/main.Verify the following values:<topic_end><topic_start>Reviewing the Gradle build configurationReview the default Gradle build file(build.gradle, located in [project]/android/app),to verify that the values are correct.<topic_end><topic_start>Under the defaultConfig block<topic_end><topic_start>Under the android blockFor more information, check out the module-level buildsection in the Gradle build file.<topic_end><topic_start>Building the app for releaseYou have two possible release formats when publishing tothe Play Store.info Note  The Google Play Store prefers the app bundle format.  For more information, check out  About Android App Bundles.<topic_end><topic_start>Build an app bundleThis section describes how to build a release app bundle.If you completed the signing steps,the app bundle will be signed.At this point, you might consider obfuscating your Dart codeto make it more difficult to reverse engineer. Obfuscatingyour code involves adding a couple flags to your build command,and maintaining additional files to de-obfuscate stack traces.From the command line:The release bundle for your app is created at[project]/build/app/outputs/bundle/release/app.aab.By default, the app bundle contains your Dart code and the Flutterruntime compiled for armeabi-v7a (ARM 32-bit), arm64-v8a(ARM 64-bit), and x86-64 (x86 64-bit).<topic_end><topic_start>Test the app bundleAn app bundle can be tested in multiple ways.This section describes two.<topic_end><topic_start>Offline using the bundle tool<topic_end><topic_start>Online using Google Play<topic_end><topic_start>Build an APKAlthough app bundles are preferred over APKs, there are storesthat don’t yet support app bundles. In this case, build a releaseAPK for each target ABI (Application Binary Interface).If you completed the signing steps, the APK will be signed.At this point, you might consider obfuscating your Dart codeto make it more difficult to reverse engineer. Obfuscatingyour code involves adding a couple flags to your build command.From the command line:Enter cd [project].Run flutter build apk --split-per-abi.(The flutter build command defaults to --release.)This command results in three APK files:Removing the --split-per-abi flag results in a fat APK that containsyour code compiled for all the target ABIs. Such APKs are larger insize than their split counterparts, causing the user to downloadnative binaries that are not applicable to their device’s architecture.<topic_end><topic_start>Install an APK on a deviceFollow these steps to install the APK on a connected Android device.From the command line:<topic_end><topic_start>Publishing to the Google Play StoreFor detailed instructions on publishing your app to the Google Play Store,check out the Google Play launch documentation.<topic_end><topic_start>Updating the app’s version numberThe default version number of the app is 1.0.0.To update it, navigate to the pubspec.yaml fileand update the following line:version: 1.0.0+1The version number is three numbers separated by dots,such as 1.0.0 in the example above, followed by an optionalbuild number such as 1 in the example above, separated by a +.Both the version and the build number can be overridden in Flutter’sbuild by specifying --build-name and --build-number, respectively.In Android, build-name is used as versionName whilebuild-number used as versionCode. For more information,check out Version your app in the Android documentation.When you rebuild the app for Android, any updates in the version numberfrom the pubspec file will update the versionName and versionCode in the local.properties file.<topic_end><topic_start>Android release FAQHere are some commonly asked questions about deployment forAndroid apps.<topic_end><topic_start>When should I build app bundles versus APKs?The Google Play Store recommends that you deploy app bundlesover APKs because they allow a more efficient delivery of theapplication to your users. However, if you’re distributingyour application by means other than the Play Store,an APK might be your only option.<topic_end><topic_start>What is a fat APK?A fat APK is a single APK that contains binaries for multipleABIs embedded within it. This has the benefit that the single APKruns on multiple architectures and thus has wider compatibility,but it has the drawback that its file size is much larger,causing users to download and store more bytes when installingyour application. When building APKs instead of app bundles,it is strongly recommended to build split APKs,as described in build an APK using the--split-per-abi flag.<topic_end><topic_start>What are the supported target architectures?When building your application in release mode,Flutter apps can be compiled for armeabi-v7a (ARM 32-bit),arm64-v8a (ARM 64-bit), and x86-64 (x86 64-bit).Flutter supports building for x86 Android through ARM emulation.<topic_end><topic_start>How do I sign the app bundle created by flutter build appbundle?See Signing the app.<topic_end><topic_start>How do I build a release from within Android Studio?In Android Studio, open the existing android/folder under your app’s folder. Then,select build.gradle (Module: app) in the project panel:Next, select the build variant. Click Build > Select Build Variantin the main menu. Select any of the variants in the Build Variantspanel (debug is the default):The resulting app bundle or APK files are located inbuild/app/outputs within your app’s folder.<topic_end><topic_start>Build and release an iOS appThis guide provides a step-by-step walkthrough of releasing aFlutter app to the App Store and TestFlight.<topic_end><topic_start>PreliminariesXcode is required to build and release your app. Youmust use a device running macOS to follow this guide.Before beginning the process of releasing your app,ensure that it meets Apple’s App Review Guidelines.To publish your app to the App Store,you must first enroll in the Apple Developer Program.You can read more about the various membership options in Apple’sChoosing a Membership guide.<topic_end><topic_start>Video overviewFor those who prefer video over text,the following video covers the same material as this guide.Release an iOS app with Flutter in 7 steps<topic_end><topic_start>Register your app on App Store ConnectManage your app’s life cycle onApp Store Connect (formerly iTunes Connect).You define your app name and description, add screenshots,set pricing, and manage releases to the App Store and TestFlight.Registering your app involves two steps: registering a uniqueBundle ID, and creating an application record on App Store Connect.For a detailed overview of App Store Connect, see theApp Store Connect guide.<topic_end><topic_start>Register a Bundle IDEvery iOS application is associated with a Bundle ID,a unique identifier registered with Apple.To register a Bundle ID for your app, follow these steps:<topic_end><topic_start>Create an application record on App Store ConnectRegister your app on App Store Connect:For a detailed overview, seeAdd an app to your account.<topic_end><topic_start>Review Xcode project settingsThis step covers reviewing the most important settingsin the Xcode workspace.For detailed procedures and descriptions, seePrepare for app distribution.Navigate to your target’s settings in Xcode:Verify the most important settings.In the Identity section of the General tab:In the Signing & Capabilities tab:In the Deployment section of the Build Settings tab:The General tab of your project settings should resemblethe following:For a detailed overview of app signing, seeCreate, export, and delete signing certificates.<topic_end><topic_start>Updating the app’s deployment versionIf you changed Deployment Target in your Xcode project,open ios/Flutter/AppframeworkInfo.plist in your Flutter appand update the MinimumOSVersion value to match.<topic_end><topic_start>Add an app iconWhen a new Flutter app is created, a placeholder icon set is created.This step covers replacing these placeholder icons with yourapp’s icons:<topic_end><topic_start>Add a launch imageSimilar to the app icon,you can also replace the placeholder launch image:<topic_end><topic_start>Create a build archive and upload to App Store ConnectDuring development, you’ve been building, debugging, and testingwith debug builds. When you’re ready to ship your app to userson the App Store or TestFlight, you need to prepare a release build.<topic_end><topic_start>Update the app’s build and version numbersThe default version number of the app is 1.0.0.To update it, navigate to the pubspec.yaml fileand update the following line:The version number is three numbers separated by dots,such as 1.0.0 in the example above, followed by an optionalbuild number such as 1 in the example above, separated by a +.Both the version and the build number can be overridden influtter build ipa by specifying --build-name and --build-number,respectively.In iOS, build-name uses CFBundleShortVersionStringwhile build-number uses CFBundleVersion.Read more about iOS versioning at Core Foundation Keyson the Apple Developer’s site.You can also override the pubspec.yaml build name and number in Xcode:<topic_end><topic_start>Create an app bundleRun flutter build ipa to produce an Xcode build archive (.xcarchive file)in your project’s build/ios/archive/ directory and an App Store appbundle (.ipa file) in build/ios/ipa.Consider adding the --obfuscate and --split-debug-info flags toobfuscate your Dart code to make it more difficultto reverse engineer.If you are not distributing to the App Store, you can optionallychoose a different export method byadding the option --export-method ad-hoc,--export-method development or --export-method enterprise.info Note  On versions of Flutter where flutter build ipa --export-method is unavailable,  open build/ios/archive/MyApp.xcarchive and follow the instructions below  to validate and distribute the app from Xcode.<topic_end><topic_start>Upload the app bundle to App Store ConnectOnce the app bundle is created, upload it toApp Store Connect by either:Install and open the Apple Transport macOS app.Drag and drop the build/ios/ipa/*.ipa app bundle into the app.Or upload the app bundle from the command line by running:Run man altool for details about how to authenticate with the App Store Connect API key.Or open build/ios/archive/MyApp.xcarchive in Xcode.Click the Validate App button. If any issues are reported,address them and produce another build. You can reuse the samebuild ID until you upload an archive.After the archive has been successfully validated, clickDistribute App.info Note  When you export your app at the end of Distribute App,  Xcode will create a directory containing  an IPA of your app and an ExportOptions.plist file.  You can create new IPAs with the same options without launching  Xcode by running  flutter build ipa --export-options-plist=path/to/ExportOptions.plist.  See xcodebuild -h for details about the keys in this property list.You can follow the status of your build in theActivities tab of your app’s details page onApp Store Connect.You should receive an email within 30 minutes notifying you thatyour build has been validated and is available to release to testerson TestFlight. At this point you can choose whether to releaseon TestFlight, or go ahead and release your app to the App Store.For more details, seeUpload an app to App Store Connect.<topic_end><topic_start>Create a build archive with Codemagic CLI toolsThis step covers creating a build archive and uploadingyour build to App Store Connect using Flutter build commandsand Codemagic CLI Tools executed in a terminalin the Flutter project directory. This allows you to create a build archivewith full control of distribution certificates in a temporary keychainisolated from your login keychain.Install the Codemagic CLI tools:You’ll need to generate an App Store Connect API Keywith App Manager access to automate operations with App Store Connect. To makesubsequent commands more concise, set the following environment variables fromthe new key: issuer id, key id, and API key file.You need to export or create an iOS Distribution certificate to code sign and package a build archive.If you have existing certificates, you can export theprivate keys by executing the following command for each certificate:Or you can create a new private key by executing the following command:Later, you can have CLI tools automatically create a new iOS Distribution from the private key.Set up a new temporary keychain to be used for code signing:Restore Login Keychain!After running keychain initialize you must run the following:keychain use-loginThis sets your login keychain as the default to avoid potentialauthentication issues with apps on your machine.Fetch the code signing files from App Store Connect:Where cert_key is either your exported iOS Distribution certificate private keyor a new private key which automatically generates a new certificate. The certificatewill be created from the private key if it doesn’t exist in App Store Connect.Now add the fetched certificates to your keychain:Update the Xcode project settings to use fetched code signing profiles:Install Flutter dependencies:Install CocoaPods dependencies:Build the Flutter the iOS project:Note that export_options.plist is the output of the xcode-project use-profiles command.Publish the app to App Store Connect:As mentioned earlier, don’t forget to set your login keychainas the default to avoid authentication issueswith apps on your machine:You should receive an email within 30 minutes notifying you thatyour build has been validated and is available to release to testerson TestFlight. At this point you can choose whether to releaseon TestFlight, or go ahead and release your app to the App Store.<topic_end><topic_start>Release your app on TestFlightTestFlight allows developers to push their appsto internal and external testers. This optional stepcovers releasing your build on TestFlight.For more details, see Distribute an app using TestFlight.<topic_end><topic_start>Release your app to the App StoreWhen you’re ready to release your app to the world,follow these steps to submit your app for review andrelease to the App Store:Apple notifies you when their app review process is complete.Your app is released according to the instructions youspecified in the Version Release section.For more details, seeDistribute an app through the App Store.<topic_end><topic_start>TroubleshootingThe Distribute your app guide provides adetailed overview of the process of releasing an app to the App Store.<topic_end><topic_start>Build and release a macOS appThis guide provides a step-by-step walkthrough of releasing aFlutter app to the App Store.<topic_end><topic_start>PreliminariesBefore beginning the process of releasing your app,ensure that it meetsApple’s App Review Guidelines.In order to publish your app to the App Store,you must first enroll in theApple Developer Program.You can read more about the variousmembership options in Apple’sChoosing a Membership guide.<topic_end><topic_start>Register your app on App Store ConnectManage your app’s life cycle onApp Store Connect (formerly iTunes Connect).You define your app name and description, add screenshots,set pricing, and manage releases to the App Store and TestFlight.Registering your app involves two steps: registering a uniqueBundle ID, and creating an application record on App Store Connect.For a detailed overview of App Store Connect, see theApp Store Connect guide.<topic_end><topic_start>Register a Bundle IDEvery macOS application is associated with a Bundle ID,a unique identifier registered with Apple.To register a Bundle ID for your app, follow these steps:<topic_end><topic_start>Create an application record on App Store ConnectRegister your app on App Store Connect:For a detailed overview,see Add an app to your account.<topic_end><topic_start>Review Xcode project settingsThis step covers reviewing the most important settingsin the Xcode workspace.For detailed procedures and descriptions, seePrepare for app distribution.Navigate to your target’s settings in Xcode:Verify the most important settings.In the Identity section:In the Deployment info section:In the Signing & Capabilities section:The General tab of your project settings should resemblethe following:For a detailed overview of app signing, seeCreate, export, and delete signing certificates.<topic_end><topic_start>Configuring the app’s name, bundle identifier and copyrightThe configuration for the product identifiers are centralized in macos/Runner/Configs/AppInfo.xcconfig. For the app’s name,set PRODUCT_NAME, for the copyright set PRODUCT_COPYRIGHT,and finally set PRODUCT_BUNDLE_IDENTIFIER for the app’sbundle identifier.<topic_end><topic_start>Updating the app’s version numberThe default version number of the app is 1.0.0.To update it, navigate to the pubspec.yaml fileand update the following line:version: 1.0.0+1The version number is three numbers separated by dots,such as 1.0.0 in the example above, followed by an optionalbuild number such as 1 in the example above, separated by a +.Both the version and the build number can be overridden in Flutter’sbuild by specifying --build-name and --build-number,respectively.In macOS, build-name uses CFBundleShortVersionStringwhile build-number uses CFBundleVersion.Read more about iOS versioning at Core Foundation Keyson the Apple Developer’s site.<topic_end><topic_start>Add an app iconWhen a new Flutter app is created, a placeholder icon set is created.This step covers replacing these placeholder icons with yourapp’s icons:<topic_end><topic_start>Create a build archive with XcodeThis step covers creating a build archive and uploadingyour build to App Store Connect using Xcode.During development, you’ve been building, debugging, and testingwith debug builds. When you’re ready to ship your app to userson the App Store or TestFlight, you need to prepare a release build.At this point, you might consider obfuscating your Dart codeto make it more difficult to reverse engineer. Obfuscatingyour code involves adding a couple flags to your build command.In Xcode, configure the app version and build:Finally, create a build archive and upload it to App Store Connect:You should receive an email within 30 minutes notifying you thatyour build has been validated and is available to release to testerson TestFlight. At this point you can choose whether to releaseon TestFlight, or go ahead and release your app to the App Store.For more details, seeUpload an app to App Store Connect.<topic_end><topic_start>Create a build archive with Codemagic CLI toolsThis step covers creating a build archive and uploadingyour build to App Store Connect using Flutter build commands and Codemagic CLI Tools executed in a terminalin the Flutter project directory.Install the Codemagic CLI tools:You’ll need to generate an App Store Connect API Keywith App Manager access to automate operations with App Store Connect. To makesubsequent commands more concise, set the following environment variables fromthe new key: issuer id, key id, and API key file.You need to export or create a Mac App Distribution and a Mac InstallerDistribution certificate to perform code signing and package a build archive.If you have existing certificates, you can export theprivate keys by executing the following command for each certificate:Or you can create a new private key by executing the following command:Later, you can have CLI tools automatically create a new Mac App Distribution andMac Installer Distribution certificate. You can use the same private key foreach new certificate.Fetch the code signing files from App Store Connect:Where cert_key is either your exported Mac App Distribution certificate private keyor a new private key which automatically generates a new certificate.If you do not have a Mac Installer Distribution certificate,you can create a new certificate by executing the following:Use cert_key of the private key you created earlier.Fetch the Mac Installer Distribution certificates:Set up a new temporary keychain to be used for code signing:Restore Login Keychain!  After running keychain initialize you must run the following:keychain use-loginThis sets your login keychain as the default to avoid potential  authentication issues with apps on your machine.Now add the fetched certificates to your keychain:Update the Xcode project settings to use fetched code signing profiles:Install Flutter dependencies:Install CocoaPods dependencies:Build the Flutter macOS project:Package the app:Publish the packaged app to App Store Connect:As mentioned earlier, don’t forget to set your login keychainas the default to avoid authentication issueswith apps on your machine:<topic_end><topic_start>Release your app on TestFlightTestFlight allows developers to push their appsto internal and external testers. This optional stepcovers releasing your build on TestFlight.<topic_end><topic_start>Distribute to registered devicesSee distribution guide to prepare an archive for distribution to designated Mac computers.<topic_end><topic_start>Release your app to the App StoreWhen you’re ready to release your app to the world,follow these steps to submit your app for review andrelease to the App Store:Apple notifies you when their app review process is complete.Your app is released according to the instructions youspecified in the Version Release section.For more details, seeDistribute an app through the App Store.<topic_end><topic_start>TroubleshootingThe Distribute your app guide provides adetailed overview of the process of releasing an app to the App Store.<topic_end><topic_start>Build and release a Linux app to the Snap StoreDuring a typical development cycle,you test an app using flutter run at the command line,or by using the Run and Debugoptions in your IDE. By default,Flutter builds a debug version of your app.When you’re ready to prepare a release version of your app,for example to publish to the Snap Store,this page can help.<topic_end><topic_start>PrerequisitesTo build and publish to the Snap Store, you need thefollowing components:<topic_end><topic_start>Set up the build environmentUse the following instructions to set up your build environment.<topic_end><topic_start>Install snapcraftAt the command line, run the following:<topic_end><topic_start>Install LXDTo install LXD, use the following command:LXD is required during the snap build process. Once installed, LXD needs to beconfigured for use. The default answers are suitable for most use cases.On the first run, LXD might not be able to connect to its socket:This means you need to add your username to the LXD(lxd) group, so log out of your session and then log back in:<topic_end><topic_start>Overview of snapcraftThe snapcraft tool builds snaps based on the instructionslisted in a snapcraft.yaml file.To get a basic understanding of snapcraft and itscore concepts, take a look at the Snap documentationand the Introduction to snapcraft.Additional links and information are listed at thebottom of this page.<topic_end><topic_start>Flutter snapcraft.yaml examplePlace the YAML file in your Flutterproject under <project root>/snap/snapcraft.yaml.(And remember that YAML files are sensitive to white space!)For example:The following sections explain the various pieces of the YAML file.<topic_end><topic_start>MetadataThis section of the snapcraft.yaml file defines anddescribes the application. The snap version isderived (adopted) from the build section.<topic_end><topic_start>Grade, confinement, and baseThis section defines how the snap is built.<topic_end><topic_start>AppsThis section defines the application(s) that exist inside the snap.There can be one or more applications per snap. This examplehas a single application—super_cool_app.When a providing snap is installed, snapd will generate security policy that will allow it to listen on the well-known DBus name on the specified bus. If the system bus is specified, snapd will also generate DBus bus policy that allows ‘root’ to own the name and any user to communicate with the service. Non-snap processes are allowed to communicate with the providing snap following traditional permissions checks. Other (consuming) snaps might only communicate with the providing snap by connecting the snaps’ interface.<topic_end><topic_start>PartsThis section defines the sources required toassemble the snap.Parts can be downloaded and built automatically using plugins.Similar to extensions, snapcraft can use various plugins(such as Python, C, Java, and Ruby) to assist in thebuilding process. Snapcraft also has some special plugins.<topic_end><topic_start>Desktop file and iconDesktop entry files are used to add an application to the desktop menu. These files specify the name and icon of your application, the categories it belongs to,related search keywords and more. These files have the extension .desktop and follow the XDG Desktop Entry Specification version 1.1.<topic_end><topic_start>Flutter super-cool-app.desktop examplePlace the .desktop file in your Flutter project under <project root>/snap/gui/super-cool-app.desktop.Notice: icon and .desktop file name must be the same as your app name inyaml file!For example:Place your icon with .png extension in your Flutter project under <project root>/snap/gui/super-cool-app.png.<topic_end><topic_start>Build the snapOnce the snapcraft.yaml file is complete,run snapcraft as follows from the root directoryof the project.To use the Multipass VM backend:To use the LXD container backend:<topic_end><topic_start>Test the snapOnce the snap is built, you’ll have a <name>.snap filein your root project directory.$ sudo snap install ./super-cool-app_0.1.0_amd64.snap –dangerous<topic_end><topic_start>PublishYou can now publish the snap.The process consists of the following:<topic_end><topic_start>Snap Store channelsThe Snap Store uses channels to differentiate amongdifferent versions of snaps.The snapcraft upload command uploads the snap file tothe store. However, before you run this command,you need to learn about the different release channels.Each channel consists of three components:<topic_end><topic_start>Snap Store automatic reviewThe Snap Store runs several automated checks againstyour snap. There might also be a manual review,depending on how the snap was built, and if there areany specific security concerns. If the checks passwithout errors, the snap becomes available in the store.<topic_end><topic_start>Additional resourcesYou can learn more from the following links on thesnapcraft.io site:<topic_end><topic_start>Build and release a Windows desktop appOne convenient approach to distributing Windows appsis the Microsoft Store.This guide provides a step-by-step walkthroughof packaging and deploying a Flutter app in this way.info Note  You are not required to publish Windows apps through the  Microsoft Store, particularly if you prefer more control  over the distribution experience or don’t want to deal  with the certification process. The Microsoft documentation  includes more information about traditional installation  approaches, including Windows Installer.<topic_end><topic_start>PreliminariesBefore beginning the process of releasinga Flutter Windows desktop app to the Microsoft Store,first confirm that it satisfies Microsoft Store Policies.Also, you must join theMicrosoft Partner Network to be able to submit apps.<topic_end><topic_start>Set up your application in the Partner CenterManage an application’s life cycle in theMicrosoft Partner Center.First, reserve the application name andensure that the required rights to the name exist.Once the name is reserved, the applicationwill be provisioned for services (such aspush notifications), and you can start adding add-ons.Options such as pricing, availability,age ratings, and category have to beconfigured together with the first submissionand are automatically retainedfor the subsequent submissions.<topic_end><topic_start>Packaging and deploymentIn order to publish an application to Microsoft Store,you must first package it.The valid formats are .msix, .msixbundle,.msixupload, .appx, .appxbundle,.appxupload, and .xap.<topic_end><topic_start>Manual packaging and deployment for the Microsoft StoreCheck out MSIX packagingto learn about packagingFlutter Windows desktop applications.Note that each product has a unique identity,which the Store assigns.If the package is being built manually,you have to include its identity detailsmanually during the packaging.The essential information can be retrievedfrom the Partner Center using the following instructions:After manually packaging the application,manually submit it to theMicrosoft Partner Center.You can do this by creating a new submission,navigating to Packages,and uploading the created application package.<topic_end><topic_start>Continuous deploymentIn addition to manually creating and deploying the package,you can automate the build, package, versioning,and deployment process using CI/CD tooling after having submittedthe application to the Microsoft Store for the first time.<topic_end><topic_start>Codemagic CI/CDCodemagic CI/CD uses themsix pub package to packageFlutter Windows desktop applications.For Flutter applications, use either theCodemagic Workflow Editoror codemagic.yamlto package the application and deploy itto the Microsoft Partner Center.Additional options (such as the list ofcapabilities and language resourcescontained in the package)can be configured using this package.For publishing, Codemagic uses thePartner Center submission API;so, Codemagic requiresassociating the Azure Active Directoryand Partner Center accounts.<topic_end><topic_start>GitHub Actions CI/CDGitHub Actions can use theMicrosoft Dev Store CLIto package applications into an MSIX and publish them to the Microsoft Store.The setup-msstore-cliGitHub Action installs the cli so that the Action can use it for packagingand publishing.As packaging the MSIX uses themsix pub package, the project’s pubspec.yamlmust contain an appropriate msix_config node.You must create an Azure AD directory from the Dev Center withglobal administrator permission.The GitHub Action requires environment secrets from the partner center.AZURE_AD_TENANT_ID, AZURE_AD_ClIENT_ID, and AZURE_AD_CLIENT_SECRETare visible on the Dev Center following the instructions for theWindows Store Publish Action.You also need the SELLER_ID secret, which can be found in the Dev Centerunder Account Settings > Organization Profile > Legal Info.The application must already be present in the Microsoft Dev Center with atleast one complete submission, and msstore init must be run once withinthe repository before the Action can be performed. Once complete, runningmsstore package .andmsstore publishin a GitHub Action packages theapplication into an MSIX and uploads it to a new submission on the dev center.The steps necessary for MSIX publishing resemble the following<topic_end><topic_start>Updating the app’s version numberFor apps published to the Microsoft Store,the version number must be set during thepackaging process.The default version number of the app is 1.0.0.0.info Note  Microsoft Store apps are not allowed to have a  Version with a revision number other than zero.  Therefore, the last number of the version must  remain zero for all releases.  Ensure that you follow Microsoft’s  versioning guidelines.For apps not published to the Microsoft Store, youcan set the app’s executable’s file and product versions.The executable’s default file version is 1.0.0.1,and its default product version is 1.0.0+1. To update these,navigate to the pubspec.yaml file and update thefollowing line:The build name is three numbers separated by dots,followed by an optional build number that is separatedby a +. In the example above, the build name is 1.0.0and the build number is 1.The build name becomes the first three numbers of thefile and product versions, while the build number becomesthe fourth number of the file and product versions.Both the build name and number can be overridden influtter build windows by specifying --build-name and--build-number, respectively.info Note  Flutter projects created before Flutter 3.3  need to be updated to set the executable’s version  information. For more information,  refer to the version migration guide.<topic_end><topic_start>Add app iconsTo update the icon of a Flutter Windowsdesktop application before packaging use thefollowing instructions:When packaging with the msix pub package,the logo path can also be configured inside the pubspec.yaml file.To update the application image in the Store listing,navigate to the Store listing step of the submissionand select Store logos.From there, you can upload the logo withthe size of 300 x 300 pixels.All uploaded images are retained for subsequent submissions.<topic_end><topic_start>Validating the application packageBefore publication to the Microsoft Store,first validate the application package locally.Windows App Certification Kitis a tool included in theWindows Software Development Kit (SDK).To validate the application:The report might contain important warnings and information,even if the certification passes.<topic_end><topic_start>Build and release a web appDuring a typical development cycle,you test an app using flutter run -d chrome(for example) at the command line.This builds a debug version of your app.This page helps you prepare a release versionof your app and covers the following topics:<topic_end><topic_start>Building the app for releaseBuild the app for deployment using theflutter build web command.You can also choose which renderer to useby using the --web-renderer option (See Web renderers).This generates the app, including the assets,and places the files into the /build/webdirectory of the project.The release build of a simple app has thefollowing structure:info Note  The canvaskit directory and its contents are only present when the  CanvasKit renderer is selected—not when the HTML renderer is selected.Launch a web server (for example,python -m http.server 8000,or by using the dhttpd package),and open the /build/web directory. Navigate tolocalhost:8000 in your browser(given the python SimpleHTTPServer example)to view the release version of your app.<topic_end><topic_start>Deploying to the webWhen you are ready to deploy your app,upload the release bundleto Firebase, the cloud, or a similar service.Here are a few possibilities, but there aremany others:<topic_end><topic_start>Deploying to Firebase HostingYou can use the Firebase CLI to build and release your Flutter app with FirebaseHosting.<topic_end><topic_start>Before you beginTo get started, install or update the Firebase CLI:<topic_end><topic_start>Initialize FirebaseEnable the web frameworks preview to the Firebase framework-aware CLI:In an empty directory or an existing Flutter project, run the initializationcommand:Answer yes when asked if you want to use a web framework.If you’re in an empty directory, you’ll be asked to choose your web framework. Choose Flutter Web.Choose your hosting source directory; this could be an existing flutter app.Select a region to host your files.Choose whether to set up automatic builds and deploys with GitHub.Deploy the app to Firebase Hosting:Running this command automatically runs flutter build web --release, so you don’t have to build your app in a separate step.To learn more, visit the official Firebase Hosting documentation forFlutter on the web.<topic_end><topic_start>Handling images on the webThe web supports the standard Image widget to display images.By design, web browsers run untrusted code without harming the host computer.This limits what you can do with images compared to mobile and desktop platforms.For more information, see Displaying images on the web.<topic_end><topic_start>Choosing a web rendererBy default, the flutter build and flutter run commandsuse the auto choice for the web renderer. This means thatyour app runs with the HTML renderer on mobile browsers andCanvasKit on desktop browsers. We recommend this combinationto optimize for the characteristics of each platform.For more information, see Web renderers.<topic_end><topic_start>MinificationMinification is handled for you when youcreate a release build.<topic_end><topic_start>Embedding a Flutter app into an HTML page<topic_end><topic_start>hostElementAdded in Flutter 3.10You can embed a Flutter web app intoany HTML element of your web page, with flutter.js and the hostElementengine initialization parameter.To tell Flutter web in which element to render, use the hostElement parameter of the initializeEnginefunction:To learn more, check out Customizing web app initialization.<topic_end><topic_start>IframeYou can embed a Flutter web app,as you would embed other content,in an iframe tag of an HTML file.In the following example, replace “URL”with the location of your HTML page:<topic_end><topic_start>PWA SupportAs of release 1.20, the Flutter template for web apps includes supportfor the core features needed for an installable, offline-capable PWA app.Flutter-based PWAs can be installed in the same way as any other web-basedPWA; the settings signaling that your Flutter app is a PWA are provided bymanifest.json, which is produced by flutter create in the web directory.PWA support remains a work in progress,so please give us feedback if you see something that doesn’t look right.<topic_end><topic_start>Continuous delivery with FlutterFollow continuous delivery best practices with Flutter to make sure yourapplication is delivered to your beta testers and validated on a frequent basiswithout resorting to manual workflows.<topic_end><topic_start>CI/CD OptionsThere are a number of continuous integration (CI) and continuous delivery (CD)options available to help automate the delivery of your application.<topic_end><topic_start>All-in-one options with built-in Flutter functionality<topic_end><topic_start>Integrating fastlane with existing workflowsYou can use fastlane with the following tooling:This guide shows how to set up fastlane and then integrate it with your existing testing and continuous integration (CI) workflows. For more information, see “Integrating fastlane with existing workflow”.<topic_end><topic_start>fastlanefastlane is an open-source tool suite to automate releases and deployments for your app.<topic_end><topic_start>Local setupIt’s recommended that you test the build and deployment process locally beforemigrating to a cloud-based system. You could also choose to perform continuousdelivery from a local machine. On iOS, follow thefastlane iOS beta deployment guide.You can specify the archive path to avoid rebuilding the project. For example:You’re now ready to perform deployments locally or migrate the deploymentprocess to a continuous integration (CI) system.<topic_end><topic_start>Running deployment locally<topic_end><topic_start>Cloud build and deploy setupFirst, follow the local setup section described in ‘Local setup’ to make surethe process works before migrating onto a cloud system like Travis.The main thing to consider is that since cloud instances are ephemeral anduntrusted, you won’t be leaving your credentials like your Play Store serviceaccount JSON or your iTunes distribution certificate on the server.Continuous Integration (CI) systems generally support encrypted environment variables to store private data. You can pass these environment variables using --dart-define MY_VAR=MY_VALUE while building the app.Take precaution not to re-echo those variable values back onto the console inyour test scripts. Those variables are also not available in pull requestsuntil they’re merged to ensure that malicious actors cannot create a pullrequest that prints these secrets out. Be careful with interactions with thesesecrets in pull requests that you accept and merge.<topic_end><topic_start>Xcode CloudXcode Cloud is a continuous integration and delivery service for building,testing, and distributing apps and frameworks for Apple platforms.<topic_end><topic_start>Requirements<topic_end><topic_start>Custom build scriptXcode Cloud recognizes custom build scripts that can be used to perform additional tasks at a designated time. It also includes a setof predefined environment variables, such as $CI_WORKSPACE, which is thelocation of your cloned repository.info Note  The temporary build environment that Xcode Cloud uses includes tools that are  part of macOS and Xcode—for example, Python—and additionally Homebrew to  support installing third-party dependencies and tools.<topic_end><topic_start>Post-clone scriptLeverage the post-clone custom build script that runs afterXcode Cloud clones your Git repository using the following instructions:Create a file at ios/ci_scripts/ci_post_clone.sh and add the content below.<code_start>#!/bin/sh# Fail this script if any subcommand fails.set -e# The default execution directory of this script is the ci_scripts directory.cd $CI_PRIMARY_REPOSITORY_PATH # change working directory to the root of your cloned repo.# Install Flutter using git.git clone https://github.com/flutter/flutter.git --depth 1 -b stable $HOME/flutterexport PATH="$PATH:$HOME/flutter/bin"# Install Flutter artifacts for iOS (--ios), or macOS (--macos) platforms.flutter precache --ios# Install Flutter dependencies.flutter pub get# Install CocoaPods using Homebrew.HOMEBREW_NO_AUTO_UPDATE=1 # disable homebrew's automatic updates.brew install cocoapods# Install CocoaPods dependencies.cd ios && pod install # run `pod install` in the `ios` directory.exit 0<code_end>This file should be added to your git repository and marked as executable.<topic_end><topic_start>Workflow configurationAn Xcode Cloud workflow defines the steps performed in the CI/CD processwhen your workflow is triggered.info Note  This requires that your project is already initialized with Git  and linked to a remote repository.To create a new workflow in Xcode, use the following instructions:Choose Product > Xcode Cloud > Create Workflow to open theCreate Workflow sheet.Select the product (app) that the workflow should be attached to, then clickthe Next button.The next sheet displays an overview of the default workflow provided by Xcode, and can be customized by clicking the Edit Workflow button.<topic_end><topic_start>Branch changesBy default Xcode suggests the Branch Changes condition that starts a new buildfor every change to your Git repository’s default branch.For your app’s iOS variant, it’s reasonable that you would want Xcode Cloud totrigger your workflow after you’ve made changes to your flutter packages, ormodified either the Dart or iOS source files within the lib\ and ios\directories.This can be achieved by using the following Files and Folders conditions:<topic_end><topic_start>Next build numberXcode Cloud defaults the build number for new workflows to 1 and incrementsit per successful build. If you’re using an existing app with a higher buildnumber, you’ll need to configure Xcode Cloud to use the correct build numberfor its builds by simply specifying the Next Build Number in your iteration.Check out Setting the next build number for Xcode Cloud builds for moreinformation.<topic_end><topic_start>Add Flutter to an existing app<topic_end><topic_start>Add-to-appIt’s sometimes not practical to rewrite your entire application inFlutter all at once. For those situations,Flutter can be integrated into your existingapplication piecemeal, as a library or module.That module can then be imported into your Android or iOS(currently supported platforms) app to render a part of yourapp’s UI in Flutter. Or, just to run shared Dart logic.In a few steps, you can bring the productivity and the expressiveness ofFlutter into your own app.The add-to-app feature supports integrating multiple instances of any screen size.This can help scenarios such as a hybrid navigation stack with mixednative and Flutter screens, or a page with multiple partial-screen Flutterviews.Having multiple Flutter instances allows each instance to maintainindependent application and UI state while using minimalmemory resources. See more in the multiple Flutters page.<topic_end><topic_start>Supported features<topic_end><topic_start>Add to Android applications<topic_end><topic_start>Add to iOS applicationsSee our add-to-app GitHub Samples repositoryfor sample projects in Android and iOS that importa Flutter module for UI.<topic_end><topic_start>Get startedTo get started, see our project integration guide forAndroid and iOS:<topic_end><topic_start>API usageAfter Flutter is integrated into your project,see our API usage guides at the following links:<topic_end><topic_start>Limitations<topic_end><topic_start>Add Flutter to Android<topic_end><topic_start>Topics<topic_end><topic_start>Integrate a Flutter module into your Android projectFlutter can be embedded into your existing Androidapplication piecemeal, as a source code Gradlesubproject or as AARs.The integration flow can be done using the Android StudioIDE with the Flutter plugin or manually.warning Warning  Your existing Android app might support architectures  such as mips or x86. Flutter currently only supports  building ahead-of-time (AOT) compiled libraries  for x86_64, armeabi-v7a, and arm64-v8a.Consider using the abiFilters Android Gradle  Plugin API to limit the supported architectures in your APK.  Doing this avoids a missing libflutter.so runtime crash,  for example:<code_start>android {  //...  defaultConfig {    ndk {      // Filter for architectures supported by Flutter.      abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64'    }  }}<code_end>The Flutter engine has an x86 and x86_64 version.  When using an emulator in debug Just-In-Time (JIT) mode,  the Flutter module still runs correctly.<topic_end><topic_start>Integrate your Flutter module<topic_end><topic_start>Integrate with Android StudioThe Android Studio IDE can help integrate your Flutter module.Using Android Studio, you can edit both your Android and Flutter codein the same IDE.You can also use IntelliJ Flutter plugin functionality likeDart code completion, hot reload, and widget inspector.Android Studio supports add-to-app flows on Android Studio 2022.2 or laterwith the Flutter plugin for IntelliJ.To build your app, the Android Studio plugin configures yourAndroid project to add your Flutter module as a dependency.Open your Android project in Android Studio.Go to File > New > New Project…. The New Project dialog displays.Click Flutter.If asked to provide your Flutter SDK path, do so and click Next.Complete the configuration of your Flutter module.If you have an existing project:If you need to create a new Flutter project:Click Finish.lightbulb Tip  By default, your project’s Project pane might show the ‘Android’ view.  If you can’t see your new Flutter files in the Project pane,  set your Project pane to display Project Files.  This shows all files without filtering.<topic_end><topic_start>Integrate without Android StudioTo integrate a Flutter module with an existing Android appmanually, without using Flutter’s Android Studio plugin,follow these steps:<topic_end><topic_start>Create a Flutter moduleLet’s assume that you have an existing Android app atsome/path/MyApp, and that you want your Flutterproject as a sibling:This creates a some/path/flutter_module/ Flutter module projectwith some Dart code to get you started and an .android/hidden subfolder. The .android folder contains anAndroid project that can both help you run a barebonesstandalone version of your Flutter module via flutter runand it’s also a wrapper that helps bootstrap the Fluttermodule an embeddable Android library.info Note  Add custom Android code to your own existing  application’s project or a plugin,  not to the module in .android/.  Changes made in your module’s .android/  directory won’t appear in your existing Android  project using the module.Do not source control the .android/ directory  since it’s autogenerated. Before building the  module on a new machine, run flutter pub get  in the flutter_module directory first to regenerate  the .android/ directory before building the  Android project using the Flutter module.info Note  To avoid Dex merging issues, flutter.androidPackage should  not be identical to your host app’s package name.<topic_end><topic_start>Java version requirementFlutter requires your project to declare compatibility with Java 11 or later.Before attempting to connect your Flutter module projectto your host Android app, ensure that your host Androidapp declares the following source compatibility within yourapp’s build.gradle file, under the android { } block.<code_start>android {  //...  compileOptions {    sourceCompatibility 11 // The minimum value    targetCompatibility 11 // The minimum value  }}<code_end><topic_end><topic_start>Centralize repository settingsStarting with Gradle 7, Android recommends using centralized repositorydeclarations in settings.gradle instead of project or module leveldeclarations in build.gradle files.Before attempting to connect your Flutter module project to yourhost Android app, make the following changes.Remove the repositories block in all of your app’s build.gradle files.Add the dependencyResolutionManagement displayed in this step to thesettings.gradle file.<topic_end><topic_start>Add the Flutter module as a dependencyAdd the Flutter module as a dependency of yourexisting app in Gradle. You can achieve this in two ways.Android archive The AAR mechanism creates generic Android AARs as intermediaries that packages your Flutter module. This is good when your downstream app builders don’t want to have the Flutter SDK installed. But, it adds one more build step if you build frequently.Module source code The source code subproject mechanism is a convenient one-click build process, but requires the Flutter SDK. This is the mechanism used by the Android Studio IDE plugin.<topic_end><topic_start>Depend on the Android Archive (AAR)This option packages your Flutter library as a generic localMaven repository composed of AARs and POMs artifacts.This option allows your team to build the host app withoutinstalling the Flutter SDK. You can then distribute theartifacts from a local or remote repository.Let’s assume you built a Flutter module atsome/path/flutter_module, and then run:Then, follow the on-screen instructions to integrate.More specifically, this command creates(by default all debug/profile/release modes)a local repository, with the following files:To depend on the AAR, the host app must be ableto find these files.To do that, edit settings.gradle in your host appso that it includes the local repository and the dependency:</br><topic_end><topic_start>Kotlin DSL based Android ProjectAfter an aar build of a Kotlin DSL-based Android project,follow these steps to add the flutter_module.Include the flutter module as a dependency in the Android project’s app/build.gradle file.<code_start>android {    buildTypes {        release {          ...        }        debug {          ...        }        create("profile") {            initWith(getByName("debug"))        }}dependencies {  // ...  debugImplementation "com.example.flutter_module:flutter_debug:1.0"  releaseImplementation 'com.example.flutter_module:flutter_release:1.0'  add("profileImplementation", "com.example.flutter_module:flutter_profile:1.0")}<code_end>The profileImplementation ID is a custom configuration to beimplemented in the app/build.gradle file of a host project.<code_start>configurations {    getByName("profileImplementation") {    }}<code_end><code_start>include(":app")dependencyResolutionManagement {    repositories {        maven(url = "https://storage.googleapis.com/download.flutter.io")        maven(url = "some/path/flutter_module_project/build/host/outputs/repo")    }}<code_end>error Important  If you’re located in China, use a mirror site rather than the  storage.googleapis.com domain. To learn more about mirror sites,  check out Using Flutter in China page.lightbulb Tip  You can also build an AAR for your Flutter module in Android Studio using  the Build > Flutter > Build AAR menu.<topic_end><topic_start>Depend on the module’s source codeThis option enables a one-step build for both yourAndroid project and Flutter project. This option isconvenient when you work on both parts simultaneouslyand rapidly iterate, but your team must install theFlutter SDK to build the host app.lightbulb Tip  By default, the host app provides the :app Gradle project.  To change the name of this project, set  flutter.hostAppProjectName in the Flutter module’s  gradle.properties file.  Include this project in the host app’s settings.gradle file.Include the Flutter module as a subproject in the host app’ssettings.gradle. This example assumes flutter_module and MyAppexist in the same directory<code_start>// Include the host app project.include ':app'                                    // assumed existing contentsetBinding(new Binding([gradle: this]))                                // newevaluate(new File(                                                     // new    settingsDir.parentFile,                                            // new    'flutter_module/.android/include_flutter.groovy'                   // new))                                                                     // new<code_end>The binding and script evaluation allows the Fluttermodule to include itself (as :flutter) and anyFlutter plugins used by the module (such as :package_info and :video_player)in the evaluation context of your settings.gradle.Introduce an implementation dependency on the Fluttermodule from your app:<code_start>dependencies {    implementation project(':flutter')}<code_end>Your app now includes the Flutter module as a dependency.Continue to the Adding a Flutter screen to an Android app guide.<topic_end><topic_start>Add a Flutter screen to an Android appThis guide describes how to add a single Flutter screen to anexisting Android app. A Flutter screen can be added as a normal,opaque screen, or as a see-through, translucent screen.Both options are described in this guide.<topic_end><topic_start>Add a normal Flutter screen<topic_end><topic_start>Step 1: Add FlutterActivity to AndroidManifest.xmlFlutter provides FlutterActivity to display a Flutterexperience within an Android app. Like any other Activity,FlutterActivity must be registered in yourAndroidManifest.xml. Add the following XML to yourAndroidManifest.xml file under your application tag:The reference to @style/LaunchTheme can be replacedby any Android theme that want to apply to your FlutterActivity.The choice of theme dictates the colors applied toAndroid’s system chrome, like Android’s navigation bar, and tothe background color of the FlutterActivity just beforethe Flutter UI renders itself for the first time.<topic_end><topic_start>Step 2: Launch FlutterActivityWith FlutterActivity registered in your manifest file,add code to launch FlutterActivity from whatever pointin your app that you’d like. The following example showsFlutterActivity being launched from an OnClickListener.info NoteMake sure to use the following import:<code_start>myButton.setOnClickListener(new OnClickListener() {  @Override  public void onClick(View v) {    startActivity(      FlutterActivity.createDefaultIntent(currentActivity)    );  }});<code_end><code_start>myButton.setOnClickListener {  startActivity(    FlutterActivity.createDefaultIntent(this)  )}<code_end>The previous example assumes that your Dart entrypointis called main(), and your initial Flutter route is ‘/’.The Dart entrypoint can’t be changed using Intent,but the initial route can be changed using Intent.The following example demonstrates how to launch aFlutterActivity that initially renders a customroute in Flutter.<code_start>myButton.addOnClickListener(new OnClickListener() {  @Override  public void onClick(View v) {    startActivity(      FlutterActivity        .withNewEngine()        .initialRoute("/my_route")        .build(currentActivity)      );  }});<code_end><code_start>myButton.setOnClickListener {  startActivity(    FlutterActivity      .withNewEngine()      .initialRoute("/my_route")      .build(this)  )}<code_end>Replace "/my_route" with your desired initial route.The use of the withNewEngine() factory methodconfigures a FlutterActivity that internally createits own FlutterEngine instance. This comes with anon-trivial initialization time. The alternative approachis to instruct FlutterActivity to use a pre-warmed,cached FlutterEngine, which minimizes Flutter’sinitialization time. That approach is discussed next.<topic_end><topic_start>Step 3: (Optional) Use a cached FlutterEngineEvery FlutterActivity creates its own FlutterEngineby default. Each FlutterEngine has a non-trivialwarm-up time. This means that launching a standardFlutterActivity comes with a brief delay before your Flutterexperience becomes visible. To minimize this delay,you can warm up a FlutterEngine before arriving atyour FlutterActivity, and then you can useyour pre-warmed FlutterEngine instead.To pre-warm a FlutterEngine, find a reasonablelocation in your app to instantiate a FlutterEngine.The following example arbitrarily pre-warms aFlutterEngine in the Application class:<code_start>public class MyApplication extends Application {  public FlutterEngine flutterEngine;  @Override  public void onCreate() {    super.onCreate();    // Instantiate a FlutterEngine.    flutterEngine = new FlutterEngine(this);    // Start executing Dart code to pre-warm the FlutterEngine.    flutterEngine.getDartExecutor().executeDartEntrypoint(      DartEntrypoint.createDefault()    );    // Cache the FlutterEngine to be used by FlutterActivity.    FlutterEngineCache      .getInstance()      .put("my_engine_id", flutterEngine);  }}<code_end><code_start>class MyApplication : Application() {  lateinit var flutterEngine : FlutterEngine  override fun onCreate() {    super.onCreate()    // Instantiate a FlutterEngine.    flutterEngine = FlutterEngine(this)    // Start executing Dart code to pre-warm the FlutterEngine.    flutterEngine.dartExecutor.executeDartEntrypoint(      DartExecutor.DartEntrypoint.createDefault()    )    // Cache the FlutterEngine to be used by FlutterActivity.    FlutterEngineCache      .getInstance()      .put("my_engine_id", flutterEngine)  }}<code_end>The ID passed to the FlutterEngineCache can be whatever you want.Make sure that you pass the same ID to any FlutterActivityor FlutterFragment that should use the cached FlutterEngine.Using FlutterActivity with a cached FlutterEngineis discussed next.info Note  To warm up a FlutterEngine, you must execute a Dart  entrypoint. Keep in mind that the moment  executeDartEntrypoint() is invoked,  your Dart entrypoint method begins executing.  If your Dart entrypoint invokes runApp()  to run a Flutter app, then your Flutter app behaves as if it  were running in a window of zero size until this  FlutterEngine is attached to a FlutterActivity,  FlutterFragment, or FlutterView. Make sure that your app  behaves appropriately between the time you warm it up and  the time you display Flutter content.With a pre-warmed, cached FlutterEngine, you now needto instruct your FlutterActivity to use the cachedFlutterEngine instead of creating a new one.To accomplish this, use FlutterActivity’s withCachedEngine()builder:<code_start>myButton.addOnClickListener(new OnClickListener() {  @Override  public void onClick(View v) {    startActivity(      FlutterActivity        .withCachedEngine("my_engine_id")        .build(currentActivity)      );  }});<code_end><code_start>myButton.setOnClickListener {  startActivity(    FlutterActivity      .withCachedEngine("my_engine_id")      .build(this)  )}<code_end>When using the withCachedEngine() factory method,pass the same ID that you used when caching the desiredFlutterEngine.Now, when you launch FlutterActivity,there is significantly less delay inthe display of Flutter content.info Note  When using a cached FlutterEngine, that FlutterEngine outlives any  FlutterActivity or FlutterFragment that displays it. Keep in  mind that Dart code begins executing as soon as you pre-warm the  FlutterEngine, and continues executing after the destruction of your  FlutterActivity/FlutterFragment. To stop executing and clear resources,  obtain your FlutterEngine from the FlutterEngineCache and destroy the  FlutterEngine with FlutterEngine.destroy().info Note  Runtime performance isn’t the only reason that you might  pre-warm and cache a FlutterEngine.  A pre-warmed FlutterEngine executes Dart code independent  from a FlutterActivity, which allows such a FlutterEngine  to be used to execute arbitrary Dart code at any moment.  Non-UI application logic can be executed in a FlutterEngine,  like networking and data caching, and in background behavior  within a Service or elsewhere. When using a FlutterEngine  to execute behavior in the background, be sure to adhere to all  Android restrictions on background execution.info Note  Flutter’s debug/release builds have drastically different  performance characteristics. To evaluate the performance  of Flutter, use a release build.<topic_end><topic_start>Initial route with a cached engineThe concept of an initial route is available when configuring aFlutterActivity or a FlutterFragment with a new FlutterEngine.However, FlutterActivity and FlutterFragment don’t offer theconcept of an initial route when using a cached engine.This is because a cached engine is expected to already berunning Dart code, which means it’s too late to configure theinitial route.Developers that would like their cached engine to beginwith a custom initial route can configure their cachedFlutterEngine to use a custom initial route just beforeexecuting the Dart entrypoint. The following exampledemonstrates the use of an initial route with a cached engine:<code_start>public class MyApplication extends Application {  @Override  public void onCreate() {    super.onCreate();    // Instantiate a FlutterEngine.    flutterEngine = new FlutterEngine(this);    // Configure an initial route.    flutterEngine.getNavigationChannel().setInitialRoute("your/route/here");    // Start executing Dart code to pre-warm the FlutterEngine.    flutterEngine.getDartExecutor().executeDartEntrypoint(      DartEntrypoint.createDefault()    );    // Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.    FlutterEngineCache      .getInstance()      .put("my_engine_id", flutterEngine);  }}<code_end><code_start>class MyApplication : Application() {  lateinit var flutterEngine : FlutterEngine  override fun onCreate() {    super.onCreate()    // Instantiate a FlutterEngine.    flutterEngine = FlutterEngine(this)    // Configure an initial route.    flutterEngine.navigationChannel.setInitialRoute("your/route/here");    // Start executing Dart code to pre-warm the FlutterEngine.    flutterEngine.dartExecutor.executeDartEntrypoint(      DartExecutor.DartEntrypoint.createDefault()    )    // Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.    FlutterEngineCache      .getInstance()      .put("my_engine_id", flutterEngine)  }}<code_end>By setting the initial route of the navigation channel, the associatedFlutterEngine displays the desired route upon initial execution of therunApp() Dart function.Changing the initial route property of the navigation channelafter the initial execution of runApp() has no effect.Developers who would like to use the same FlutterEnginebetween different Activitys and Fragments and switchthe route between those displays need to set up a method channel andexplicitly instruct their Dart code to change Navigator routes.<topic_end><topic_start>Add a translucent Flutter screenMost full-screen Flutter experiences are opaque.However, some apps would like to deploy a Flutterscreen that looks like a modal, for example,a dialog or bottom sheet. Flutter supports translucentFlutterActivitys out of the box.To make your FlutterActivity translucent,make the following changes to the regular process ofcreating and launching a FlutterActivity.<topic_end><topic_start>Step 1: Use a theme with translucencyAndroid requires a special theme property for Activitys that renderwith a translucent background. Create or update an Android theme with thefollowing property:Then, apply the translucent theme to your FlutterActivity.Your FlutterActivity now supports translucency.Next, you need to launch your FlutterActivitywith explicit transparency support.<topic_end><topic_start>Step 2: Start FlutterActivity with transparencyTo launch your FlutterActivity with a transparent background,pass the appropriate BackgroundMode to the IntentBuilder:<code_start>// Using a new FlutterEngine.startActivity(  FlutterActivity    .withNewEngine()    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)    .build(context));// Using a cached FlutterEngine.startActivity(  FlutterActivity    .withCachedEngine("my_engine_id")    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)    .build(context));<code_end><code_start>// Using a new FlutterEngine.startActivity(  FlutterActivity    .withNewEngine()    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)    .build(this));// Using a cached FlutterEngine.startActivity(  FlutterActivity    .withCachedEngine("my_engine_id")    .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)    .build(this));<code_end>You now have a FlutterActivity with a transparent background.info Note  Make sure that your Flutter content also includes a  translucent background. If your Flutter UI paints a  solid background color, then it still appears as  though your FlutterActivity has an opaque background.<topic_end><topic_start>Add a Flutter Fragment to an Android appThis guide describes how to add a Flutter Fragment to an existingAndroid app.  In Android, a Fragment represents a modularpiece of a larger UI. A Fragment might be used to presenta sliding drawer, tabbed content, a page in a ViewPager,or it might simply represent a normal screen in asingle-Activity app. Flutter provides a FlutterFragmentso that developers can present a Flutter experience any placethat they can use a regular Fragment.If an Activity is equally applicable for your application needs,consider using a FlutterActivity instead of aFlutterFragment, which is quicker and easier to use.FlutterFragment allows developers to control the followingdetails of the Flutter experience within the Fragment:FlutterFragment also comes with a number of calls thatmust be forwarded from its surrounding Activity.These calls allow Flutter to react appropriately to OS events.All varieties of FlutterFragment, and its requirements,are described in this guide.<topic_end><topic_start>Add a FlutterFragment to an Activity with a new FlutterEngineThe first thing to do to use a FlutterFragment is to add it to a hostActivity.To add a FlutterFragment to a host Activity, instantiate andattach an instance of FlutterFragment in onCreate() within theActivity, or at another time that works for your app:<code_start>public class MyActivity extends FragmentActivity {    // Define a tag String to represent the FlutterFragment within this    // Activity's FragmentManager. This value can be whatever you'd like.    private static final String TAG_FLUTTER_FRAGMENT = "flutter_fragment";    // Declare a local variable to reference the FlutterFragment so that you    // can forward calls to it later.    private FlutterFragment flutterFragment;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        // Inflate a layout that has a container for your FlutterFragment.        // For this example, assume that a FrameLayout exists with an ID of        // R.id.fragment_container.        setContentView(R.layout.my_activity_layout);        // Get a reference to the Activity's FragmentManager to add a new        // FlutterFragment, or find an existing one.        FragmentManager fragmentManager = getSupportFragmentManager();        // Attempt to find an existing FlutterFragment,        // in case this is not the first time that onCreate() was run.        flutterFragment = (FlutterFragment) fragmentManager            .findFragmentByTag(TAG_FLUTTER_FRAGMENT);        // Create and attach a FlutterFragment if one does not exist.        if (flutterFragment == null) {            flutterFragment = FlutterFragment.createDefault();            fragmentManager                .beginTransaction()                .add(                    R.id.fragment_container,                    flutterFragment,                    TAG_FLUTTER_FRAGMENT                )                .commit();        }    }}<code_end><code_start>class MyActivity : FragmentActivity() {  companion object {    // Define a tag String to represent the FlutterFragment within this    // Activity's FragmentManager. This value can be whatever you'd like.    private const val TAG_FLUTTER_FRAGMENT = "flutter_fragment"  }  // Declare a local variable to reference the FlutterFragment so that you  // can forward calls to it later.  private var flutterFragment: FlutterFragment? = null  override fun onCreate(savedInstanceState: Bundle?) {    super.onCreate(savedInstanceState)    // Inflate a layout that has a container for your FlutterFragment. For    // this example, assume that a FrameLayout exists with an ID of    // R.id.fragment_container.    setContentView(R.layout.my_activity_layout)    // Get a reference to the Activity's FragmentManager to add a new    // FlutterFragment, or find an existing one.    val fragmentManager: FragmentManager = supportFragmentManager    // Attempt to find an existing FlutterFragment, in case this is not the    // first time that onCreate() was run.    flutterFragment = fragmentManager      .findFragmentByTag(TAG_FLUTTER_FRAGMENT) as FlutterFragment?    // Create and attach a FlutterFragment if one does not exist.    if (flutterFragment == null) {      var newFlutterFragment = FlutterFragment.createDefault()      flutterFragment = newFlutterFragment      fragmentManager        .beginTransaction()        .add(          R.id.fragment_container,          newFlutterFragment,          TAG_FLUTTER_FRAGMENT        )        .commit()    }  }}<code_end>The previous code is sufficient to render a Flutter UIthat begins with a call to your main() Dart entrypoint,an initial Flutter route of /, and a new FlutterEngine.However, this code is not sufficient to achieve all expectedFlutter behavior. Flutter depends on various OS signals thatmust  be forwarded from your host Activity to FlutterFragment.These calls are shown in the following example:<code_start>public class MyActivity extends FragmentActivity {    @Override    public void onPostResume() {        super.onPostResume();        flutterFragment.onPostResume();    }    @Override    protected void onNewIntent(@NonNull Intent intent) {        flutterFragment.onNewIntent(intent);    }    @Override    public void onBackPressed() {        flutterFragment.onBackPressed();    }    @Override    public void onRequestPermissionsResult(        int requestCode,        @NonNull String[] permissions,        @NonNull int[] grantResults    ) {        flutterFragment.onRequestPermissionsResult(            requestCode,            permissions,            grantResults        );    }    @Override    public void onActivityResult(        int requestCode,        int resultCode,        @Nullable Intent data    ) {        super.onActivityResult(requestCode, resultCode, data);        flutterFragment.onActivityResult(            requestCode,            resultCode,            data        );    }    @Override    public void onUserLeaveHint() {        flutterFragment.onUserLeaveHint();    }    @Override    public void onTrimMemory(int level) {        super.onTrimMemory(level);        flutterFragment.onTrimMemory(level);    }}<code_end><code_start>class MyActivity : FragmentActivity() {  override fun onPostResume() {    super.onPostResume()    flutterFragment!!.onPostResume()  }  override fun onNewIntent(@NonNull intent: Intent) {    flutterFragment!!.onNewIntent(intent)  }  override fun onBackPressed() {    flutterFragment!!.onBackPressed()  }  override fun onRequestPermissionsResult(    requestCode: Int,    permissions: Array<String?>,    grantResults: IntArray  ) {    flutterFragment!!.onRequestPermissionsResult(      requestCode,      permissions,      grantResults    )  }  override fun onActivityResult(    requestCode: Int,    resultCode: Int,    data: Intent?  ) {    super.onActivityResult(requestCode, resultCode, data)    flutterFragment!!.onActivityResult(      requestCode,      resultCode,      data    )  }  override fun onUserLeaveHint() {    flutterFragment!!.onUserLeaveHint()  }  override fun onTrimMemory(level: Int) {    super.onTrimMemory(level)    flutterFragment!!.onTrimMemory(level)  }}<code_end>With the OS signals forwarded to Flutter,your FlutterFragment works as expected.You have now added a FlutterFragment to your existing Android app.The simplest integration path uses a new FlutterEngine,which comes with a non-trivial initialization time,leading to a blank UI until Flutter isinitialized and rendered the first time.Most of this time overhead can be avoided by usinga cached, pre-warmed FlutterEngine, which is discussed next.<topic_end><topic_start>Using a pre-warmed FlutterEngineBy default, a FlutterFragment creates its own instanceof a FlutterEngine, which requires non-trivial warm-up time.This means your user sees a blank Fragment for a brief moment.You can mitigate most of this warm-up time byusing an existing, pre-warmed instance of FlutterEngine.To use a pre-warmed FlutterEngine in a FlutterFragment,instantiate a FlutterFragment with the withCachedEngine()factory method.<code_start>// Somewhere in your app, before your FlutterFragment is needed,// like in the Application class ...// Instantiate a FlutterEngine.FlutterEngine flutterEngine = new FlutterEngine(context);// Start executing Dart code in the FlutterEngine.flutterEngine.getDartExecutor().executeDartEntrypoint(    DartEntrypoint.createDefault());// Cache the pre-warmed FlutterEngine to be used later by FlutterFragment.FlutterEngineCache  .getInstance()  .put("my_engine_id", flutterEngine);<code_end><code_start>FlutterFragment.withCachedEngine("my_engine_id").build();<code_end><code_start>// Somewhere in your app, before your FlutterFragment is needed,// like in the Application class ...// Instantiate a FlutterEngine.val flutterEngine = FlutterEngine(context)// Start executing Dart code in the FlutterEngine.flutterEngine.getDartExecutor().executeDartEntrypoint(    DartEntrypoint.createDefault())// Cache the pre-warmed FlutterEngine to be used later by FlutterFragment.FlutterEngineCache  .getInstance()  .put("my_engine_id", flutterEngine)<code_end><code_start>FlutterFragment.withCachedEngine("my_engine_id").build()<code_end>FlutterFragment internally knows about FlutterEngineCacheand retrieves the pre-warmed FlutterEngine based on the IDgiven to withCachedEngine().By providing a pre-warmed FlutterEngine,as previously shown, your app renders thefirst Flutter frame as quickly as possible.<topic_end><topic_start>Initial route with a cached engineThe concept of an initial route is available when configuring aFlutterActivity or a FlutterFragment with a new FlutterEngine.However, FlutterActivity and FlutterFragment don’t offer theconcept of an initial route when using a cached engine.This is because a cached engine is expected to already berunning Dart code, which means it’s too late to configure theinitial route.Developers that would like their cached engine to beginwith a custom initial route can configure their cachedFlutterEngine to use a custom initial route just beforeexecuting the Dart entrypoint. The following exampledemonstrates the use of an initial route with a cached engine:<code_start>public class MyApplication extends Application {  @Override  public void onCreate() {    super.onCreate();    // Instantiate a FlutterEngine.    flutterEngine = new FlutterEngine(this);    // Configure an initial route.    flutterEngine.getNavigationChannel().setInitialRoute("your/route/here");    // Start executing Dart code to pre-warm the FlutterEngine.    flutterEngine.getDartExecutor().executeDartEntrypoint(      DartEntrypoint.createDefault()    );    // Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.    FlutterEngineCache      .getInstance()      .put("my_engine_id", flutterEngine);  }}<code_end><code_start>class MyApplication : Application() {  lateinit var flutterEngine : FlutterEngine  override fun onCreate() {    super.onCreate()    // Instantiate a FlutterEngine.    flutterEngine = FlutterEngine(this)    // Configure an initial route.    flutterEngine.navigationChannel.setInitialRoute("your/route/here");    // Start executing Dart code to pre-warm the FlutterEngine.    flutterEngine.dartExecutor.executeDartEntrypoint(      DartExecutor.DartEntrypoint.createDefault()    )    // Cache the FlutterEngine to be used by FlutterActivity or FlutterFragment.    FlutterEngineCache      .getInstance()      .put("my_engine_id", flutterEngine)  }}<code_end>By setting the initial route of the navigation channel, the associatedFlutterEngine displays the desired route upon initial execution of therunApp() Dart function.Changing the initial route property of the navigation channelafter the initial execution of runApp() has no effect.Developers who would like to use the same FlutterEnginebetween different Activitys and Fragments and switchthe route between those displays need to set up a method channel andexplicitly instruct their Dart code to change Navigator routes.<topic_end><topic_start>Display a splash screenThe initial display of Flutter content requires some wait time,even if a pre-warmed FlutterEngine is used.To help improve the user experience aroundthis brief waiting period, Flutter supports thedisplay of a splash screen (also known as “launch screen”) until Flutterrenders its first frame. For instructions about how to show a launchscreen, see the splash screen guide.<topic_end><topic_start>Run Flutter with a specified initial routeAn Android app might contain many independent Flutter experiences,running in different FlutterFragments, with differentFlutterEngines. In these scenarios,it’s common for each Flutter experience to begin with differentinitial routes (routes other than /).To facilitate this, FlutterFragment’s Builderallows you to specify a desired initial route, as shown:<code_start>// With a new FlutterEngine.FlutterFragment flutterFragment = FlutterFragment.withNewEngine()    .initialRoute("myInitialRoute/")    .build();<code_end><code_start>// With a new FlutterEngine.val flutterFragment = FlutterFragment.withNewEngine()    .initialRoute("myInitialRoute/")    .build()<code_end>info NoteFlutterFragment’s initial route property has no effect when a pre-warmed  FlutterEngine is used because the pre-warmed FlutterEngine already  chose an initial route. The initial route can be chosen explicitly when  pre-warming a FlutterEngine.<topic_end><topic_start>Run Flutter from a specified entrypointSimilar to varying initial routes, differentFlutterFragments might want to execute differentDart entrypoints. In a typical Flutter app, there is only oneDart entrypoint: main(), but you can define other entrypoints.FlutterFragment supports specification of the desiredDart entrypoint to execute for the given Flutter experience.To specify an entrypoint, build FlutterFragment, as shown:<code_start>FlutterFragment flutterFragment = FlutterFragment.withNewEngine()    .dartEntrypoint("mySpecialEntrypoint")    .build();<code_end><code_start>val flutterFragment = FlutterFragment.withNewEngine()    .dartEntrypoint("mySpecialEntrypoint")    .build()<code_end>The FlutterFragment configuration results in the executionof a Dart entrypoint called mySpecialEntrypoint().Notice that the parentheses () arenot included in the dartEntrypoint String name.info NoteFlutterFragment’s Dart entrypoint property has no effect  when a pre-warmed FlutterEngine is used because the  pre-warmed FlutterEngine already executed a Dart entrypoint.  The Dart entrypoint can be chosen explicitly when pre-warming  a FlutterEngine.<topic_end><topic_start>Control FlutterFragment’s render modeFlutterFragment can either use a SurfaceView to render itsFlutter content, or it can use a TextureView.The default is SurfaceView, which is significantlybetter for performance than TextureView. However, SurfaceViewcan’t be interleaved in the middle of an Android View hierarchy.A SurfaceView must either be the bottommost View in the hierarchy,or the topmost View in the hierarchy.Additionally, on Android versions before Android N,SurfaceViews can’t be animated because their layout and renderingaren’t synchronized with the rest of the View hierarchy.If either of these use cases are requirements for your app,then you need to use TextureView instead of SurfaceView.Select a TextureView by building a FlutterFragment with atexture RenderMode:<code_start>// With a new FlutterEngine.FlutterFragment flutterFragment = FlutterFragment.withNewEngine()    .renderMode(FlutterView.RenderMode.texture)    .build();// With a cached FlutterEngine.FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")    .renderMode(FlutterView.RenderMode.texture)    .build();<code_end><code_start>// With a new FlutterEngine.val flutterFragment = FlutterFragment.withNewEngine()    .renderMode(FlutterView.RenderMode.texture)    .build()// With a cached FlutterEngine.val flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")    .renderMode(FlutterView.RenderMode.texture)    .build()<code_end>Using the configuration shown, the resulting FlutterFragmentrenders its UI to a TextureView.<topic_end><topic_start>Display a FlutterFragment with transparencyBy default, FlutterFragment renders with an opaque background,using a SurfaceView. (See “Control FlutterFragment’s rendermode.”) That background is black for any pixels that aren’t painted by Flutter. Rendering with an opaque background isthe preferred rendering mode for performance reasons.Flutter rendering with transparency on Android negativelyaffects performance. However, there are many designs thatrequire transparent pixels in the Flutter experience thatshow through to the underlying Android UI. For this reason,Flutter supports translucency in a FlutterFragment.info Note  Both SurfaceView and TextureView support transparency.  However, when a SurfaceView is instructed to render with  transparency, it positions itself at a higher z-index than  all other Android Views, which means it appears  above all other Views. This is a limitation of SurfaceView.  If it’s acceptable to render your Flutter experience on top  of all other content, then FlutterFragment’s default  RenderMode of surface is the RenderMode that you  should use. However, if you need to display Android Views both  above and below your Flutter experience, then you must specify a  RenderMode of texture.  See “Control FlutterFragment’s render mode”  for information about controlling the RenderMode.To enable transparency for a FlutterFragment,build it with the following configuration:<code_start>// Using a new FlutterEngine.FlutterFragment flutterFragment = FlutterFragment.withNewEngine()    .transparencyMode(FlutterView.TransparencyMode.transparent)    .build();// Using a cached FlutterEngine.FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")    .transparencyMode(FlutterView.TransparencyMode.transparent)    .build();<code_end><code_start>// Using a new FlutterEngine.val flutterFragment = FlutterFragment.withNewEngine()    .transparencyMode(FlutterView.TransparencyMode.transparent)    .build()// Using a cached FlutterEngine.val flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")    .transparencyMode(FlutterView.TransparencyMode.transparent)    .build()<code_end><topic_end><topic_start>The relationship between FlutterFragment and its ActivitySome apps choose to use Fragments as entire Android screens.In these apps, it would be reasonable for a Fragment tocontrol system chrome like Android’s status bar,navigation bar, and orientation.In other apps, Fragments are used to represent onlya portion of a UI. A FlutterFragment might be used toimplement the inside of a drawer, a video player,or a single card. In these situations, it would beinappropriate for the FlutterFragment to affectAndroid’s system chrome because there are other UIpieces within the same Window.FlutterFragment comes with a concept that helpsdifferentiate between the case when a FlutterFragmentshould be able to control its host Activity, and thecases when a FlutterFragment should only affect itsown behavior. To prevent a FlutterFragment fromexposing its Activity to Flutter plugins, and toprevent Flutter from controlling the Activity’s system UI,use the shouldAttachEngineToActivity() method inFlutterFragment’s Builder, as shown:<code_start>// Using a new FlutterEngine.FlutterFragment flutterFragment = FlutterFragment.withNewEngine()    .shouldAttachEngineToActivity(false)    .build();// Using a cached FlutterEngine.FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")    .shouldAttachEngineToActivity(false)    .build();<code_end><code_start>// Using a new FlutterEngine.val flutterFragment = FlutterFragment.withNewEngine()    .shouldAttachEngineToActivity(false)    .build()// Using a cached FlutterEngine.val flutterFragment = FlutterFragment.withCachedEngine("my_engine_id")    .shouldAttachEngineToActivity(false)    .build()<code_end>Passing false to the shouldAttachEngineToActivity()Builder method prevents Flutter from interacting withthe surrounding Activity. The default value is true,which allows Flutter and Flutter plugins to interact with thesurrounding Activity.info Note  Some plugins might expect or require an Activity reference.  Ensure that none of your plugins require an Activity  before you disable access.<topic_end><topic_start>Add a Flutter View to an Android appwarning Warning  Integrating via a FlutterView  is advanced usage and requires manually creating custom, application specific  bindings.Integrating via a FlutterViewrequires a bit more work than via FlutterActivity and FlutterFragment previouslydescribed.Fundamentally, the Flutter framework on the Dart side requires access to variousactivity-level events and lifecycles to function. Since the FlutterView (whichis an android.view.View)can be added to any activity which is owned by the developer’s applicationand since the FlutterView doesn’t have access to activity level events, thedeveloper must bridge those connections manually to the FlutterEngine.How you choose to feed your application’s activities’ events to the FlutterViewwill be specific to your application.<topic_end><topic_start>A sampleUnlike the guides for FlutterActivity and FlutterFragment, the FlutterViewintegration could be better demonstrated with a sample project.A sample project is at https://github.com/flutter/samples/tree/main/add_to_app/android_viewto document a simple FlutterView integration where FlutterViews are usedfor some of the cells in a RecycleView list of cards as seen in the gif above.<topic_end><topic_start>General approachThe general gist of the FlutterView-level integration is that you must recreatethe various interactions between your Activity, the FlutterViewand the FlutterEnginepresent in the FlutterActivityAndFragmentDelegatein your own application’s code. The connections made in the FlutterActivityAndFragmentDelegateare done automatically when using a FlutterActivityor a FlutterFragment,but since the FlutterViewin this case is being added to an Activity or Fragment in your application,you must recreate the connections manually. Otherwise, the FlutterViewwill not render anything or have other missing functionalities.A sample FlutterViewEngineclass shows one such possible implementation of an application-specificconnection between an Activity, a FlutterViewand a FlutterEngine.<topic_end><topic_start>APIs to implementThe absolute minimum implementation needed for Flutter to draw anything at allis to:The reverse detachFromFlutterEngine and other lifecycle methods on the LifecycleChannelclass must also be called to not leak resources when the FlutterView or Activityis no longer visible.In addition, see the remaining implementation in the FlutterViewEnginedemo class or in the FlutterActivityAndFragmentDelegateto ensure a correct functioning of other features such as clipboards, systemUI overlay, plugins etc.<topic_end><topic_start>Manage plugins and dependencies in add-to-appThis guide describes how to set up your project to consumeplugins and how to manage your Gradle library dependenciesbetween your existing Android app and your Flutter module’s plugins.<topic_end><topic_start>A. Simple scenarioIn the simple cases:There are no additional steps needed. Your add-to-appmodule will work the same way as a full-Flutter app.Whether you integrate using Android Studio, Gradle subproject or AARs,transitive Android Gradle libraries are automaticallybundled as needed into your outer existing app.<topic_end><topic_start>B. Plugins needing project editsSome plugins require you to make some edits to theAndroid side of your project.For example, the integration instructions for thefirebase_crashlytics plugin require manualedits to your Android wrapper project’s build.gradle file.For full-Flutter apps, these edits are done in yourFlutter project’s /android/ directory.In the case of a Flutter module, there are only Dartfiles in your module project. Perform those AndroidGradle file edits on your outer, existing Androidapp rather than in your Flutter module.info Note  Astute readers might notice that the Flutter module  directory also contains an .android and an  .ios directory. Those directories are Flutter-tool-generated  and are only meant to bootstrap Flutter into generic  Android or iOS libraries. They should not be edited or checked-in.  This allows Flutter to improve the integration point should  there be bugs or updates needed with new versions of Gradle,  Android, Android Gradle Plugin, etc.For advanced users, if more modularity is needed and you must  not leak knowledge of your Flutter module’s dependencies into  your outer host app, you can rewrap and repackage your Flutter  module’s Gradle library inside another native Android Gradle  library that depends on the Flutter module’s Gradle library.  You can make your Android specific changes such as editing the  AndroidManifest.xml, Gradle files or adding more Java files  in that wrapper library.<topic_end><topic_start>C. Merging librariesThe scenario that requires slightly more attention is ifyour existing Android application already depends on thesame Android library that your Flutter moduledoes (transitively via a plugin).For instance, your existing app’s Gradle might already have:<code_start>…dependencies {  …  implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'  …}…<code_end>And your Flutter module also depends onfirebase_crashlytics via pubspec.yaml:<code_start>…dependencies:  …  firebase_crashlytics: ^0.1.3  ……<code_end>This plugin usage transitively adds a Gradle dependency again viafirebase_crashlytics v0.1.3’s own Gradle file:<code_start>…dependencies {  …  implementation 'com.crashlytics.sdk.android:crashlytics:2.9.9'  …}…<code_end>The two com.crashlytics.sdk.android:crashlytics dependenciesmight not be the same version. In this example,the host app requested v2.10.1 and the Fluttermodule plugin requested v2.9.9.By default, Gradle v5resolves dependency version conflictsby using the newest version of the library.This is generally ok as long as there are no APIor implementation breaking changes between the versions.For example, you might use the new Crashlytics libraryin your existing app as follows:<code_start>…dependencies {  …  implementation 'com.google.firebase:firebase-crashlytics:17.0.0-beta03  …}…<code_end>This approach won’t work since there are major API differencesbetween the Crashlytics’ Gradle library versionv17.0.0-beta03 and v2.9.9.For Gradle libraries that follow semantic versioning,you can generally avoid compilation and runtime errorsby using the same major semantic version in yourexisting app and Flutter module plugin.<topic_end><topic_start>Add Flutter to iOS<topic_end><topic_start>Topics<topic_end><topic_start>Integrate a Flutter module into your iOS projectFlutter UI components can be incrementally added into your existing iOSapplication as embedded frameworks. There are a few ways to embed Flutter in your existing application.Use the CocoaPods dependency manager and installed Flutter SDK.  In this case, the flutter_module is compiled from  the source each time the app is built. (Recommended.)Create frameworks for the Flutter engine, your compiled Dart code,  and all Flutter plugins. Here, you manually embed the frameworks,  and update your existing application’s build settings in Xcode.  This can be useful for teams that don’t want to require every developer  to have the Flutter SDK and Cocoapods installed locally.Create frameworks for your compiled Dart code,  and all Flutter plugins. Use CocoaPods for the Flutter engine.   With this option, embed the frameworks for your application  and the plugins in Xcode, but distribute the  Flutter engine as a CocoaPods podspec.  This is similar to the second option, but it provides  an alternative to distributing the large Flutter.xcframework.For examples using an app built with UIKit, see the iOS directories in the add_to_app code samples. For an example using SwiftUI, see the iOS directory in News Feed App.<topic_end><topic_start>System requirementsYour development environment must meet themacOS system requirements for Flutterwith Xcode installed.Flutter supports iOS 12 and later.Additionally, you will need CocoaPodsversion 1.10 or later.<topic_end><topic_start>Create a Flutter moduleTo embed Flutter into your existing application, using any of the methods mentioned above, first create a Flutter module.From the command line, run:A Flutter module project is created at some/path/my_flutter/.If you are using the first method mentioned above, the module should be created in the same parent directory as your existing iOS app.From the Flutter module directory, you can run the same fluttercommands you would in any other Flutter project,like flutter run --debug or flutter build ios.You can also run the module inAndroid Studio/IntelliJ or VS Code withthe Flutter and Dart plugins. This project contains asingle-view example version of your module before it’sembedded in your existing application,which is useful for incrementallytesting the Flutter-only parts of your code.<topic_end><topic_start>Module organizationThe my_flutter module directory structure is similar to anormal Flutter application:Add your Dart code to the lib/ directory.Add Flutter dependencies to my_flutter/pubspec.yaml,including Flutter packages and plugins.The .ios/ hidden subfolder contains an Xcode workspace whereyou can run a standalone version of your module.It is a wrapper project to bootstrap your Flutter code,and contains helper scripts to facilitate building frameworks orembedding the module into your existing application with CocoaPods.info Note  Add custom iOS code to your own existing application’s  project or to a plugin, not to the module’s .ios/  directory. Changes made in your module’s .ios/  directory don’t appear in your existing iOS project  using the module, and might be overwritten by Flutter.Do not source control the .ios/ directory since it’s autogenerated.  Before building the module on a new machine, run flutter pub get  in the my_flutter directory first to regenerate the .ios/  directory before building iOS project using the Flutter module.<topic_end><topic_start>Embed the Flutter module in your existing applicationAfter you have developed your Flutter module,you can embed it using the methods described at the top of the page.info Note  You can run in Debug mode on a simulator or a real device,  and Release on a real device. Learn more about  Flutter’s build modesTo leverage Flutter debugging functionality   such as hot reload, see  Debugging your add-to-app module.Using Flutter increases your app size.<topic_end><topic_start>Option A - Embed with CocoaPods and the Flutter SDKThis method requires every developer working on yourproject to have a locally installed version of the Flutter SDK.The Flutter module is compiled from source each time the app is built.Simply build your application in Xcode to automaticallyrun the script to embed your Dart and plugin code.This allows rapid iteration with the most up-to-dateversion of your Flutter module without running additionalcommands outside of Xcode.The following example assumes that your existingapplication and the Flutter module are in siblingdirectories. If you have a different directory structure,you might need to adjust the relative paths.If your existing application (MyApp) doesn’talready have a Podfile, run pod init in theMyApp directory to create one. You can find more details on using CocoaPods in the CocoaPods getting started guide.Add the following lines to your Podfile:<code_start>flutter_application_path = '../my_flutter'load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')<code_end>For each Podfile target that needs toembed Flutter, call install_all_flutter_pods(flutter_application_path).<code_start>target 'MyApp' do  install_all_flutter_pods(flutter_application_path)end<code_end>In the Podfile’s post_install block, call flutter_post_install(installer).<code_start>post_install do |installer|  flutter_post_install(installer) if defined?(flutter_post_install)end<code_end>info Note  The flutter_post_install method (added in Flutter 3.1.0),  adds build settings to support native Apple Silicon arm64 iOS simulators.  Include the if defined?(flutter_post_install) check to ensure your Podfile  is valid if you are running on older versions of Flutter that don’t have this method.Run pod install.info Note  When you change the Flutter plugin dependencies in  my_flutter/pubspec.yaml, run flutter pub get  in your Flutter module directory to refresh the list  of plugins read by the podhelper.rb script.  Then, run pod install again from  your application at some/path/MyApp.The podhelper.rb script embeds your plugins,Flutter.framework, and App.framework into your project.Your app’s Debug and Release build configurations embedthe Debug or Release build modes of Flutter, respectively.Add a Profile build configurationto your app to test in profile mode.lightbulb TipFlutter.framework is the bundle for the Flutter engine,  and App.framework is the compiled Dart code for this project.Open MyApp.xcworkspace in Xcode.You can now build the project using ⌘B.<topic_end><topic_start>Option B - Embed frameworks in XcodeAlternatively, you can generate the necessary frameworksand embed them in your application by manually editingyour existing Xcode project. You might do this if members of yourteam can’t locally install Flutter SDK and CocoaPods,or if you don’t want to use CocoaPodsas a dependency manager in your existing applications.You must run flutter build ios-frameworkevery time you make code changes in your Flutter module.The following example assumes that you want to generate theframeworks to some/path/MyApp/Flutter/.warning Warning  Always use Flutter.xcframework and App.xcframework  from the same directory. Mixing .xcframework imports  from different directories (such as Profile/Flutter.xcframework  with Debug/App.xcframework) causes runtime crashes.Link and embed the generated frameworks into your existingapplication in Xcode.  There are multiple ways to dothis—use the method that is best for your project.<topic_end><topic_start>Link on the frameworksFor example, you can drag the frameworks fromsome/path/MyApp/Flutter/Release/ in Finderinto your target’s BuildSettings > Build Phases > Link Binary With Libraries.In the target’s build settings, add $(PROJECT_DIR)/Flutter/Release/to the Framework Search Paths (FRAMEWORK_SEARCH_PATHS).lightbulb Tip  To use the simulator, you will need to   embed the Debug version of the Flutter frameworks in your  Debug build configuration. To do this   you can use $(PROJECT_DIR)/Flutter/$(CONFIGURATION)  in the Framework Search Paths (FRAMEWORK_SEARCH_PATHS)  build setting. This embeds the Release frameworks in the Release configuration,   and the Debug frameworks in the Debug Configuration.You must also open MyApp.xcodeproj/project.pbxproj (from Finder)   and replace path = Flutter/Release/example.xcframework;  with path = "Flutter/$(CONFIGURATION)/example.xcframework";  for all added frameworks. (Note the added ".)<topic_end><topic_start>Embed the frameworksThe generated dynamic frameworks must be embeddedinto your app to be loaded at runtime.error Important  Plugins might produce static or dynamic frameworks.  Static frameworks should be linked on, but never embedded.  If you embed a static framework into your application,  your application is not publishable to the App Store  and fails with a  Found an unexpected Mach-O header code archive error.After linking the frameworks, you should see them in the Frameworks, Libraries, and Embedded Contentsection of your target’s General settings. To embed the dynamic frameworksselect Embed & Sign.They will then appear under Embed Frameworks within Build Phases as follows:You should now be able to build the project in Xcode using ⌘B.<topic_end><topic_start>Option C - Embed application and plugin frameworks in Xcode and Flutter framework with CocoaPodsAlternatively, instead of distributing the large Flutter.xcframeworkto other developers, machines, or continuous integration systems,you can instead generate Flutter as CocoaPods podspec by addingthe flag --cocoapods. This produces a Flutter.podspecinstead of an engine Flutter.xcframework.The App.xcframework and plugin frameworks are generatedas described in Option B.To generate the Flutter.podspec and frameworks, run the following from the command line in the root of your Flutter module:Host apps using CocoaPods can add Flutter to their Podfile:<code_start>pod 'Flutter', :podspec => 'some/path/MyApp/Flutter/[build mode]/Flutter.podspec'<code_end>info Note  You must hard code the [build mode] value.  For example, use Debug if you need to use  flutter attach and Release when you’re ready to ship.Link and embed the generated App.xcframework,FlutterPluginRegistrant.xcframework,and any plugin frameworks into your existing applicationas described in Option B.<topic_end><topic_start>Local Network Privacy PermissionsOn iOS 14 and higher, enable the Dart multicast DNSservice in the Debug version of your appto add debugging functionalities such as hot-reload andDevTools via flutter attach.warning Warning  This service must not be enabled in the Release  version of your app, or you might experience App Store rejections.One way to do this is to maintain a separate copy of your app’s Info.plist perbuild configuration. The following instructions assumethe default Debug and Release.Adjust the names as needed depending on your app’s build configurations.Rename your app’s Info.plist to Info-Debug.plist.Make a copy of it called Info-Release.plist and add it to your Xcode project.In Info-Debug.plist only add the key NSBonjourServicesand set the value to an array with the string _dartVmService._tcp.Note Xcode will display this as “Bonjour services”.Optionally, add the key NSLocalNetworkUsageDescription set to yourdesired customized permission dialog text.In your target’s build settings, change the Info.plist File(INFOPLIST_FILE) setting path from path/to/Info.plist to path/to/Info-$(CONFIGURATION).plist.This will resolve to the path Info-Debug.plist in Debug andInfo-Release.plist in Release.Alternatively, you can explicitly set the Debug path to Info-Debug.plistand the Release path to Info-Release.plist.If the Info-Release.plist copy is in your target’s Build Settings > Build Phases > Copy BundleResources build phase, remove it.The first Flutter screen loaded by your Debug app will now promptfor local network permission. The permission can also be allowed by enablingSettings > Privacy > Local Network > Your App.<topic_end><topic_start>Apple Silicon (arm64 Macs)On an Apple Silicon (M1) Mac, the host app builds for an arm64 simulator.While Flutter supports arm64 simulators, some plugins might not. If you useone of these plugins, you might see a compilation error like Undefined symbolsfor architecture arm64 and you must exclude arm64 from the simulatorarchitectures in your host app.In your host app target, find the Excluded Architectures (EXCLUDED_ARCHS) build setting.Click the right arrow disclosure indicator icon to expand the available build configurations.Hover over Debug and click the plus icon. Change Any SDK to Any iOS Simulator SDK.Add arm64 to the build settings value.When done correctly, Xcode will add "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; to your project.pbxproj file.Repeat for any iOS unit test targets.<topic_end><topic_start>DevelopmentYou can now add a Flutter screen to your existing application.<topic_end><topic_start>Add a Flutter screen to an iOS appThis guide describes how to add a single Flutter screen to an existing iOS app.<topic_end><topic_start>Start a FlutterEngine and FlutterViewControllerTo launch a Flutter screen from an existing iOS, you start aFlutterEngine and a FlutterViewController.The FlutterEngine serves as a host to the Dart VM and your Flutter runtime,  and the FlutterViewController attaches to a FlutterEngine to pass   input events into Flutter and to display frames rendered by the  FlutterEngine.The FlutterEngine might have the same lifespan as yourFlutterViewController or outlive your FlutterViewController.lightbulb Tip  It’s generally recommended to pre-warm a long-lived  FlutterEngine for your application because:See Loading sequence and performancefor more analysis on the latency and memorytrade-offs of pre-warming an engine.<topic_end><topic_start>Create a FlutterEngineWhere you create a FlutterEngine depends on your host app.In this example, we create a FlutterEngine object inside a SwiftUI ObservableObject. We then pass this FlutterEngine into a ContentView using the  environmentObject() property.<code_start>import SwiftUI import Flutter // The following library connects plugins with iOS platform code to this app. import FlutterPluginRegistrant class FlutterDependencies: ObservableObject {   let flutterEngine = FlutterEngine(name: "my flutter engine")   init(){     // Runs the default Dart entrypoint with a default Flutter route.     flutterEngine.run()     // Connects plugins with iOS platform code to this app.     GeneratedPluginRegistrant.register(with: self.flutterEngine);   } } @main struct MyApp: App {   // flutterDependencies will be injected using EnvironmentObject.   @StateObject var flutterDependencies = FlutterDependencies()     var body: some Scene {       WindowGroup {         ContentView().environmentObject(flutterDependencies)       }     } }<code_end>As an example, we demonstrate creating aFlutterEngine, exposed as a property, on app startup inthe app delegate.<code_start>import UIKitimport Flutter// The following library connects plugins with iOS platform code to this app.import FlutterPluginRegistrant@UIApplicationMainclass AppDelegate: FlutterAppDelegate { // More on the FlutterAppDelegate.  lazy var flutterEngine = FlutterEngine(name: "my flutter engine")  override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {    // Runs the default Dart entrypoint with a default Flutter route.    flutterEngine.run();    // Connects plugins with iOS platform code to this app.    GeneratedPluginRegistrant.register(with: self.flutterEngine);    return super.application(application, didFinishLaunchingWithOptions: launchOptions);  }}<code_end>In this example, we create a FlutterEngine object inside a SwiftUI ObservableObject. We then pass this FlutterEngine into a ContentView using the environmentObject() property.<code_start>@import UIKit;@import Flutter;@interface AppDelegate : FlutterAppDelegate // More on the FlutterAppDelegate below.@property (nonatomic,strong) FlutterEngine *flutterEngine;@end<code_end><code_start>// The following library connects plugins with iOS platform code to this app.#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h>#import "AppDelegate.h"@implementation AppDelegate- (BOOL)application:(UIApplication *)application    didFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id> *)launchOptions {  self.flutterEngine = [[FlutterEngine alloc] initWithName:@"my flutter engine"];  // Runs the default Dart entrypoint with a default Flutter route.  [self.flutterEngine run];  // Connects plugins with iOS platform code to this app.  [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];  return [super application:application didFinishLaunchingWithOptions:launchOptions];}@end<code_end><topic_end><topic_start>Show a FlutterViewController with your FlutterEngineThe following example shows a generic ContentView with aButton hooked to present a FlutterViewController.The FlutterViewController constructor takes the pre-warmed FlutterEngine as an argument. FlutterEngine is passed in as an EnvironmentObject via flutterDependencies.<code_start>import SwiftUIimport Flutterstruct ContentView: View {  // Flutter dependencies are passed in an EnvironmentObject.  @EnvironmentObject var flutterDependencies: FlutterDependencies  // Button is created to call the showFlutter function when pressed.  var body: some View {    Button("Show Flutter!") {      showFlutter()    }  }func showFlutter() {    // Get the RootViewController.    guard      let windowScene = UIApplication.shared.connectedScenes        .first(where: { $0.activationState == .foregroundActive && $0 is UIWindowScene }) as? UIWindowScene,      let window = windowScene.windows.first(where: \.isKeyWindow),      let rootViewController = window.rootViewController    else { return }    // Create the FlutterViewController.    let flutterViewController = FlutterViewController(      engine: flutterDependencies.flutterEngine,      nibName: nil,      bundle: nil)    flutterViewController.modalPresentationStyle = .overCurrentContext    flutterViewController.isViewOpaque = false    rootViewController.present(flutterViewController, animated: true)  }}<code_end>The following example shows a generic ViewController with aUIButton hooked to present a FlutterViewController.The FlutterViewController uses the FlutterEngine instancecreated in the AppDelegate.<code_start>import UIKitimport Flutterclass ViewController: UIViewController {  override func viewDidLoad() {    super.viewDidLoad()    // Make a button to call the showFlutter function when pressed.    let button = UIButton(type:UIButton.ButtonType.custom)    button.addTarget(self, action: #selector(showFlutter), for: .touchUpInside)    button.setTitle("Show Flutter!", for: UIControl.State.normal)    button.frame = CGRect(x: 80.0, y: 210.0, width: 160.0, height: 40.0)    button.backgroundColor = UIColor.blue    self.view.addSubview(button)  }  @objc func showFlutter() {    let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine    let flutterViewController =        FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)    present(flutterViewController, animated: true, completion: nil)  }}<code_end>The following example shows a generic ViewController with aUIButton hooked to present a FlutterViewController.The FlutterViewController uses the FlutterEngine instancecreated in the AppDelegate.<code_start>@import Flutter;#import "AppDelegate.h"#import "ViewController.h"@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    // Make a button to call the showFlutter function when pressed.    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];    [button addTarget:self               action:@selector(showFlutter)     forControlEvents:UIControlEventTouchUpInside];    [button setTitle:@"Show Flutter!" forState:UIControlStateNormal];    button.backgroundColor = UIColor.blueColor;    button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);    [self.view addSubview:button];}- (void)showFlutter {    FlutterEngine *flutterEngine =        ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine;    FlutterViewController *flutterViewController =        [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];    [self presentViewController:flutterViewController animated:YES completion:nil];}@end<code_end>Now, you have a Flutter screen embedded in your iOS app.info Note  Using the previous example, the default main()  entrypoint function of your default Dart library  would run when calling run on the  FlutterEngine created in the AppDelegate.<topic_end><topic_start>Alternatively - Create a FlutterViewController with an implicit FlutterEngineAs an alternative to the previous example, you can let theFlutterViewController implicitly create its own FlutterEngine withoutpre-warming one ahead of time.This is not usually recommended because creating aFlutterEngine on-demand could introduce a noticeablelatency between when the FlutterViewController ispresented and when it renders its first frame. This could, however, beuseful if the Flutter screen is rarely shown, when there are no goodheuristics to determine when the Dart VM should be started, and when Flutterdoesn’t need to persist state between view controllers.To let the FlutterViewController present without an existingFlutterEngine, omit the FlutterEngine construction, and create theFlutterViewController without an engine reference.<code_start>// Existing code omitted.func showFlutter() {  let flutterViewController = FlutterViewController(project: nil, nibName: nil, bundle: nil)  present(flutterViewController, animated: true, completion: nil)}<code_end><code_start>// Existing code omitted.- (void)showFlutter {  FlutterViewController *flutterViewController =      [[FlutterViewController alloc] initWithProject:nil nibName:nil bundle:nil];  [self presentViewController:flutterViewController animated:YES completion:nil];}@end<code_end>See Loading sequence and performancefor more explorations on latency and memory usage.<topic_end><topic_start>Using the FlutterAppDelegateLetting your application’s UIApplicationDelegate subclassFlutterAppDelegate is recommended but not required.The FlutterAppDelegate performs functions such as:<topic_end><topic_start>Creating a FlutterAppDelegate subclassCreating a subclass of the FlutterAppDelegate in UIKit apps was shown in the Start a FlutterEngine and FlutterViewController section. In a SwiftUI app, you can create a subclass of the FlutterAppDelegate that conforms to the ObservableObject protocol as follows:Then, in your view, the AppDelegateis accessible as an EnvironmentObject.<topic_end><topic_start>If you can’t directly make FlutterAppDelegate a subclassIf your app delegate can’t directly make FlutterAppDelegate a subclass,make your app delegate implement the FlutterAppLifeCycleProviderprotocol in order to make sure your plugins receive the necessary callbacks.Otherwise, plugins that depend on these events might have undefined behavior.For instance:<code_start>import Foundationimport Flutterclass AppDelegate: UIResponder, UIApplicationDelegate, FlutterAppLifeCycleProvider, ObservableObject {  private let lifecycleDelegate = FlutterPluginAppLifeCycleDelegate()  let flutterEngine = FlutterEngine(name: "flutter_nps_engine")  override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {    flutterEngine.run()    return lifecycleDelegate.application(application, didFinishLaunchingWithOptions: launchOptions ?? [:])  }  func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {    lifecycleDelegate.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)  }  func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {    lifecycleDelegate.application(application, didFailToRegisterForRemoteNotificationsWithError: error)  }  func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {    lifecycleDelegate.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)  }  func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {    return lifecycleDelegate.application(app, open: url, options: options)  }  func application(_ application: UIApplication, handleOpen url: URL) -> Bool {    return lifecycleDelegate.application(application, handleOpen: url)  }  func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {    return lifecycleDelegate.application(application, open: url, sourceApplication: sourceApplication ?? "", annotation: annotation)  }  func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {    lifecycleDelegate.application(application, performActionFor: shortcutItem, completionHandler: completionHandler)  }  func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {    lifecycleDelegate.application(application, handleEventsForBackgroundURLSession: identifier, completionHandler: completionHandler)  }  func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {    lifecycleDelegate.application(application, performFetchWithCompletionHandler: completionHandler)  }  func add(_ delegate: FlutterApplicationLifeCycleDelegate) {    lifecycleDelegate.add(delegate)  }}<code_end><code_start>@import Flutter;@import UIKit;@import FlutterPluginRegistrant;@interface AppDelegate : UIResponder <UIApplicationDelegate, FlutterAppLifeCycleProvider>@property (strong, nonatomic) UIWindow *window;@property (nonatomic,strong) FlutterEngine *flutterEngine;@end<code_end>The implementation should delegate mostly to aFlutterPluginAppLifeCycleDelegate:<code_start>@interface AppDelegate ()@property (nonatomic, strong) FlutterPluginAppLifeCycleDelegate* lifeCycleDelegate;@end@implementation AppDelegate- (instancetype)init {    if (self = [super init]) {        _lifeCycleDelegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];    }    return self;}- (BOOL)application:(UIApplication*)applicationdidFinishLaunchingWithOptions:(NSDictionary<UIApplicationLaunchOptionsKey, id>*))launchOptions {    self.flutterEngine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:nil];    [self.flutterEngine runWithEntrypoint:nil];    [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];    return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions];}// Returns the key window's rootViewController, if it's a FlutterViewController.// Otherwise, returns nil.- (FlutterViewController*)rootFlutterViewController {    UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController;    if ([viewController isKindOfClass:[FlutterViewController class]]) {        return (FlutterViewController*)viewController;    }    return nil;}- (void)application:(UIApplication*)applicationdidRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings {    [_lifeCycleDelegate application:applicationdidRegisterUserNotificationSettings:notificationSettings];}- (void)application:(UIApplication*)applicationdidRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {    [_lifeCycleDelegate application:applicationdidRegisterForRemoteNotificationsWithDeviceToken:deviceToken];}- (void)application:(UIApplication*)applicationdidReceiveRemoteNotification:(NSDictionary*)userInfofetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {    [_lifeCycleDelegate application:application       didReceiveRemoteNotification:userInfo             fetchCompletionHandler:completionHandler];}- (BOOL)application:(UIApplication*)application            openURL:(NSURL*)url            options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options {    return [_lifeCycleDelegate application:application openURL:url options:options];}- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {    return [_lifeCycleDelegate application:application handleOpenURL:url];}- (BOOL)application:(UIApplication*)application            openURL:(NSURL*)url  sourceApplication:(NSString*)sourceApplication         annotation:(id)annotation {    return [_lifeCycleDelegate application:application                                   openURL:url                         sourceApplication:sourceApplication                                annotation:annotation];}- (void)application:(UIApplication*)applicationperformActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem  completionHandler:(void (^)(BOOL succeeded))completionHandler {    [_lifeCycleDelegate application:application       performActionForShortcutItem:shortcutItem                  completionHandler:completionHandler];}- (void)application:(UIApplication*)applicationhandleEventsForBackgroundURLSession:(nonnull NSString*)identifier  completionHandler:(nonnull void (^)(void))completionHandler {    [_lifeCycleDelegate application:applicationhandleEventsForBackgroundURLSession:identifier                  completionHandler:completionHandler];}- (void)application:(UIApplication*)applicationperformFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {    [_lifeCycleDelegate application:application performFetchWithCompletionHandler:completionHandler];}- (void)addApplicationLifeCycleDelegate:(NSObject<FlutterPlugin>*)delegate {    [_lifeCycleDelegate addDelegate:delegate];}@end<code_end><topic_end><topic_start>Launch optionsThe examples demonstrate running Flutter using the default launch settings.In order to customize your Flutter runtime,you can also specify the Dart entrypoint, library, and route.<topic_end><topic_start>Dart entrypointCalling run on a FlutterEngine, by default,runs the main() Dart functionof your lib/main.dart file.You can also run a different entrypoint function by usingrunWithEntrypoint with an NSString specifyinga different Dart function.info Note  Dart entrypoint functions other than main()  must be annotated with the following in order to  not be tree-shaken away when compiling:<topic_end><topic_start>Dart libraryIn addition to specifying a Dart function, you can specify an entrypointfunction in a specific file.For instance the following runs myOtherEntrypoint()in lib/other_file.dart instead of main() in lib/main.dart:<topic_end><topic_start>RouteStarting in Flutter version 1.22, an initial route can be set for your FlutterWidgetsApp when constructing the FlutterEngine or theFlutterViewController.This code sets your dart:ui’s window.defaultRouteNameto "/onboarding" instead of "/".Alternatively, to construct a FlutterViewController directly without pre-warminga FlutterEngine:lightbulb Tip  In order to imperatively change your current Flutter  route from the platform side after the FlutterEngine  is already running, use pushRoute()  or popRoute() on the FlutterViewController.To pop the iOS route from the Flutter side,  call SystemNavigator.pop().See Navigation and routing for more about Flutter’s routes.<topic_end><topic_start>OtherThe previous example only illustrates a few ways to customizehow a Flutter instance is initiated. Using platform channels,you’re free to push data or prepare your Flutter environmentin any way you’d like, before presenting the Flutter UI using aFlutterViewController.<topic_end><topic_start>Debug your add-to-app moduleOnce you’ve integrated the Flutter module to your project and used Flutter’splatform APIs to run the Flutter engine and/or UI,you can then build and run your Android or iOS app the same wayyou run normal Android or iOS apps.However, Flutter is now powering the UI in places where you’re showing aFlutterActivity or FlutterViewController.<topic_end><topic_start>DebuggingYou might be used to having your suite of favorite Flutter debugging toolsavailable to you automatically when running flutter run or an equivalentcommand from an IDE. But you can also use all your Flutterdebugging functionalities such as hot reload, performanceoverlays, DevTools, and setting breakpoints in add-to-app scenarios.These functionalities are provided by the flutter attach mechanism.flutter attach can be initiated through different pathways,such as through the SDK’s CLI tools,through VS Code or IntelliJ/Android Studio.flutter attach can connect as soon as you run your FlutterEngine, andremains attached until your FlutterEngine is disposed. But you can invokeflutter attach before starting your engine. flutter attach waits forthe next available Dart VM that is hosted by your engine.<topic_end><topic_start>TerminalRun flutter attach or flutter attach -d deviceId to attach from the terminal.<topic_end><topic_start>VS Code<topic_end><topic_start>Build the iOS version of the Flutter app in the TerminalTo generate the needed iOS platform dependencies,run the flutter build command.<topic_end><topic_start>Start debugging with VS Code firstIf you use VS Code to debug most of your code, start with this section.<topic_end><topic_start>Start the Dart debugger in VS CodeTo open the Flutter app directory, go toFile >Open Folder… and choose the my_app directory.Open the lib/main.dart file.If you can build an app for more than one device,you must select the device first.Go toView >Command Palette…You can also press Ctrl / Cmd +Shift + P.Type flutter select.Click the Flutter: Select Device command.Choose your target device.Click the debug icon().This opens the Debug pane and launches the app.Wait for the app to launch on the device and for the debug pane toindicate Connected.The debugger takes longer to launch the first time.Subsequent launches start faster.This Flutter app contains two buttons:<topic_end><topic_start>Attach to the Flutter process in XcodeTo attach to the Flutter app, go toDebug >Attach to Process >Runner.Runner should be at the top of the Attach to Process menuunder the Likely Targets heading.<topic_end><topic_start>Start debugging with Xcode firstIf you use Xcode to debug most of your code, start with this section.<topic_end><topic_start>Start the Xcode debuggerOpen ios/Runner.xcworkspace from your Flutter app directory.Select the correct device using the Scheme menu in the toolbar.If you have no preference, choose iPhone Pro 14.Run this Runner as a normal app in Xcode.When the run completes, the Debug area at the bottom of Xcode displays a message with the Dart VM service URI. It resembles the following response:Copy the Dart VM service URI.<topic_end><topic_start>Attach to the Dart VM in VS CodeTo open the command palette, go to View >Command Palette…You can also press Cmd + Shift + P.Type debug.Click the Debug: Attach to Flutter on Device command.In the Paste an VM Service URI box, paste the URI you copied from Xcode and press Enter.You can also create a .vscode/launch.json file in your Flutter module project.This enables you to attach using the Run > Start Debugging command or F5:<topic_end><topic_start>IntelliJ / Android StudioSelect the device on which the Flutter module runs so flutter attach filters for the right start signals.<topic_end><topic_start>Wireless debuggingYou can debug your app wirelessly on an iOS or Android deviceusing flutter attach.<topic_end><topic_start>iOSOn iOS, you must follow the steps below:Ensure that your device is wirelessly connected to Xcodeas described in the iOS setup guide.Open Xcode > Product > Scheme > Edit SchemeSelect the Arguments tabAdd either --vm-service-host=0.0.0.0 for IPv4, or --vm-service-host=::0 for IPv6 as a launch argumentYou can determine if you’re on an IPv6 network by opening your Mac’s Settings > Wi-Fi > Details (of the network you’re connected to) > TCP/IP and check to see if there is an IPv6 address section.<topic_end><topic_start>AndroidEnsure that your device is wirelessly connected to Android Studio as described in the Android setup guide.<topic_end><topic_start>Multiple Flutter screens or views<topic_end><topic_start>ScenariosIf you’re integrating Flutter into an existing app,or gradually migrating an existing app to use Flutter,you might find yourself wanting to add multipleFlutter instances to the same project.In particular, this can be useful in thefollowing scenarios:The advantage of using multiple Flutter instances is that eachinstance is independent and maintains its own internal navigationstack, UI, and application states. This simplifies the overall application code’sresponsibility for state keeping and improves modularity. More details on thescenarios motivating the usage of multiple Flutters can be found atflutter.dev/go/multiple-flutters.Flutter is optimized for this scenario, with a low incrementalmemory cost (~180kB) for adding additional Flutter instances. This fixed costreduction allows the multiple Flutter instance pattern to be used more liberallyin your add-to-app integration.<topic_end><topic_start>ComponentsThe primary API for adding multiple Flutter instances on both Android and iOSis based on a new FlutterEngineGroup class (Android API, iOS API)to construct FlutterEngines, rather than the FlutterEngineconstructors used previously.Whereas the FlutterEngine API was direct and easier to consume, theFlutterEngine spawned from the same FlutterEngineGroup have the performanceadvantage of sharing many of the common, reusable resources such as the GPUcontext, font metrics, and isolate group snapshot, leading to a faster initialrendering latency and lower memory footprint.FlutterEngines spawned from FlutterEngineGroup can be used to connect to UI classes like FlutterActivity or FlutterViewController in the same way as normally constructed cached FlutterEngines.The first FlutterEngine spawned from the FlutterEngineGroup doesn’t needto continue surviving in order for subsequent FlutterEngines to shareresources as long as there’s at least 1 living FlutterEngine at alltimes.Creating the very first FlutterEngine from a FlutterEngineGroup hasthe same performance characteristics as constructing aFlutterEngine using the constructors did previously.When all FlutterEngines from a FlutterEngineGroup are destroyed, the nextFlutterEngine created has the same performance characteristics as the veryfirst engine.The FlutterEngineGroup itself doesn’t need to live beyond all of the spawnedengines. Destroying the FlutterEngineGroup doesn’t affect existing spawnedFlutterEngines but does remove the ability to spawn additionalFlutterEngines that share resources with existing spawned engines.<topic_end><topic_start>CommunicationCommunication between Flutter instances is handled using platform channels(or Pigeon) through the host platform. To see our roadmap on communication,or other planned work on enhancing multiple Flutter instances, check outIssue 72009.<topic_end><topic_start>SamplesYou can find a sample demonstrating how to use FlutterEngineGroupon both Android and iOS on GitHub.<topic_end><topic_start>Load sequence, performance, and memoryThis page describes the breakdown of the steps involvedto show a Flutter UI. Knowing this, you can make better,more informed decisions about when to pre-warm the Flutter engine,which operations are possible at which stage,and the latency and memory costs of those operations.<topic_end><topic_start>Loading FlutterAndroid and iOS apps (the two supported platforms forintegrating into existing apps), full Flutter apps,and add-to-app patterns have a similar sequence ofconceptual loading steps when displaying the Flutter UI.<topic_end><topic_start>Finding the Flutter resourcesFlutter’s engine runtime and your application’s compiledDart code are both bundled as shared libraries on Androidand iOS. The first step of loading Flutter is to find thoseresources in your .apk/.ipa/.app (along with other Flutterassets such as images, fonts, and JIT code, if applicable).This happens when you construct a FlutterEngine for thefirst time on both Androidand iOS APIs.<topic_end><topic_start>Loading the Flutter libraryAfter it’s found, the engine’s shared libraries are memory loadedonce per process.On Android, this also happens when theFlutterEngine is constructed because theJNI connectors need to reference the Flutter C++ library.On iOS, this happens when theFlutterEngine is first run,such as by running runWithEntrypoint:.<topic_end><topic_start>Starting the Dart VMThe Dart runtime is responsible for managing Dart memory andconcurrency for your Dart code. In JIT mode,it’s additionally responsible for compilingthe Dart source code into machine code during runtime.A single Dart runtime exists per application session onAndroid and iOS.A one-time Dart VM start is done when constructing theFlutterEngine for the first time onAndroid and when running a Dart entrypointfor the first time on iOS.At this point, your Dart code’s snapshotis also loaded into memory from your application’s files.This is a generic process that also occurs if you used theDart SDK directly, without the Flutter engine.The Dart VM never shuts down after it’s started.<topic_end><topic_start>Creating and running a Dart IsolateAfter the Dart runtime is initialized,the Flutter engine’s usage of the Dartruntime is the next step.This is done by starting a Dart Isolate in the Dart runtime.The isolate is Dart’s container for memory and threads.A number of auxiliary threads on the host platform arealso created at this point to support the isolate, suchas a thread for offloading GPU handling and another for image decoding.One isolate exists per FlutterEngine instance, and multiple isolatescan be hosted by the same Dart VM.On Android, this happens when you callDartExecutor.executeDartEntrypoint()on a FlutterEngine instance.On iOS, this happens when you call runWithEntrypoint:on a FlutterEngine.At this point, your Dart code’s selected entrypoint(the main() function of your Dart library’s main.dart file,by default) is executed. If you called theFlutter function runApp() in your main() function,then your Flutter app or your library’s widget tree is also createdand built. If you need to prevent certain functionalities from executingin your Flutter code, then the AppLifecycleState.detachedenum value indicates that the FlutterEngine isn’t attachedto any UI components such as a FlutterViewControlleron iOS or a FlutterActivity on Android.<topic_end><topic_start>Attaching a UI to the Flutter engineA standard, full Flutter app moves to reach this state assoon as the app is launched.In an add-to-app scenario,this happens when you attach a FlutterEngineto a UI component such as by calling startActivity()with an Intent built using FlutterActivity.withCachedEngine()on Android. Or, by presenting a FlutterViewControllerinitialized by using initWithEngine: nibName: bundle:on iOS.This is also the case if a Flutter UI component was launched withoutpre-warming a FlutterEngine such as withFlutterActivity.createDefaultIntent() on Android,or with FlutterViewController initWithProject: nibName: bundle:on iOS. An implicit FlutterEngine is created in these cases.Behind the scene, both platform’s UI components provide theFlutterEngine with a rendering surface such as aSurface on Android or a CAEAGLLayer or CAMetalLayeron iOS.At this point, the Layer tree generated by your Flutterprogram, per frame, is converted intoOpenGL (or Vulkan or Metal) GPU instructions.<topic_end><topic_start>Memory and latencyShowing a Flutter UI has a non-trivial latency cost.This cost can be lessened by starting the Flutter engineahead of time.The most relevant choice for add-to-app scenarios is for youto decide when to pre-load a FlutterEngine(that is, to load the Flutter library, start the Dart VM,and run entrypoint in an isolate), and what the memory and latencycost is of that pre-warm. You also need to know how the pre-warmaffects the memory and latency cost of rendering a first Flutterframe when the UI component is subsequently attachedto that FlutterEngine.As of Flutter v1.10.3, and testing on a low-end 2015 class devicein release-AOT mode, pre-warming the FlutterEngine costs:A Flutter UI can be attached during the pre-warm.The remaining time is joined to the time-to-first-frame latency.Memory-wise, a cost sample (variable,depending on the use case) could be:Latency-wise,a cost sample (variable, depending on the use case) could be:The FlutterEngine should be pre-warmed late enough to delay thememory consumption needed but early enough to avoid combining theFlutter engine start-up time with the first frame latency ofshowing Flutter.The exact timing depends on the app’s structure and heuristics.An example would be to load the Flutter engine in the screenbefore the screen is drawn by Flutter.Given an engine pre-warm, the first frame cost on UI attach is:Memory-wise, the cost is primarily the graphical memory buffer used forrendering and is dependent on the screen size.Latency-wise, the cost is primarily waiting for the OS callback to provideFlutter with a rendering surface and compiling the remaining shader programsthat are not pre-emptively predictable. This is a one-time cost.When the Flutter UI component is released, the UI-related memory is freed.This doesn’t affect the Flutter state, which lives in the FlutterEngine(unless the FlutterEngine is also released).For performance details on creating more than one FlutterEngine,see multiple Flutters.<topic_end><topic_start>Android Studio and IntelliJ<topic_end><topic_start>Installation and setupFollow the Set up an editorinstructions to install the Dart and Flutter plugins.<topic_end><topic_start>Updating the pluginsUpdates to the plugins are shipped on a regular basis.You should be prompted in the IDE when an update is available.To check for updates manually:<topic_end><topic_start>Creating projectsYou can create a new project in one of several ways.<topic_end><topic_start>Creating a new projectCreating a new Flutter project from the Flutter starter app templatediffers between Android Studio and IntelliJ.In Android Studio:In IntelliJ:<topic_end><topic_start>Setting the company domainWhen creating a new app, some Flutter IDE plugins ask for an  organization name in reverse domain order,  something like com.example. Along with the name of the app,  this is used as the package name for Android, and the Bundle ID for iOS  when the app is released. If you think you might ever release this app,  it is better to specify these now. They cannot be changed once the app  is released. Your organization name should be unique.<topic_end><topic_start>Opening a project from existing source codeTo open an existing Flutter project:Click Open.error Important  Do not use the New > Project from existing sources  option for Flutter projects.<topic_end><topic_start>Editing code and viewing issuesThe Flutter plugin performs code analysis that enables the following:<topic_end><topic_start>Running and debugginginfo Note  You can debug your app in a few ways.The instructions below describe features available in Android  Studio and IntelliJ. For information on launching DevTools,  see Running DevTools from Android Studio in the  DevTools docs.Running and debugging are controlled from the main toolbar:<topic_end><topic_start>Selecting a targetWhen a Flutter project is open in the IDE, you should see a set ofFlutter-specific buttons on the right-hand side of the toolbar.info Note  If the Run and Debug buttons are disabled, and no targets are listed,  Flutter has not been able to discover any connected iOS or  Android devices or simulators.  You need to connect a device, or start a simulator, to proceed.<topic_end><topic_start>Run app without breakpoints<topic_end><topic_start>Run app with breakpoints<topic_end><topic_start>Fast edit and refresh development cycleFlutter offers a best-in-class developer cycle enabling you to see the effectof your changes almost instantly with the Stateful Hot Reload feature.To learn more, check out Hot reload.<topic_end><topic_start>Show performance datainfo Note  To examine performance issues in Flutter, see the  Timeline view.To view the performance data, including the widget rebuildinformation, start the app in Debug mode, and then openthe Performance tool window usingView > Tool Windows > Flutter Performance.To see the stats about which widgets are being rebuilt, and how often,click Show widget rebuild information in the Performance pane.The exact count of the rebuilds for this frame displays in the secondcolumn from the right. For a high number of rebuilds, a yellow spinningcircle displays. The column to the far right shows how many times awidget was rebuilt since entering the current screen.For widgets that aren’t rebuilt, a solid grey circle displays.Otherwise, a grey spinning circle displays.The app shown in this screenshot has been designed to deliver  poor performance, and the rebuild profiler gives you a clue  about what is happening in the frame that might cause poor  performance. The widget rebuild profiler is not a diagnostic  tool, by itself, about poor performance.The purpose of this feature is to make you aware when widgets arerebuilding—you might not realize that this is happening when justlooking at the code. If widgets are rebuilding that you didn’t expect,it’s probably a sign that you should refactor your code by splittingup large build methods into multiple widgets.This tool can help you debug at least four common performance issues:The whole screen (or large pieces of it) are built by a singleStatefulWidget, causing unnecessary UI building. Split up theUI into smaller widgets with smaller build() functions.Offscreen widgets are being rebuilt. This can happen, for example,when a ListView is nested in a tall Column that extends offscreen.Or when the RepaintBoundary is not set for a list that extendsoffscreen, causing the whole list to be redrawn.The build() function for an AnimatedBuilder draws a subtree thatdoes not need to be animated, causing unnecessary rebuilds of staticobjects.An Opacity widget is placed unnecessarily high in the widget tree.Or, an Opacity animation is created by directly manipulating theopacity property of the Opacity widget, causing the widget itselfand its subtree to rebuild.You can click on a line in the table to navigate to the linein the source where the widget is created. As the code runs,the spinning icons also display in the code pane to help youvisualize which rebuilds are happening.Note that numerous rebuilds doesn’t necessarily indicate a problem.Typically you should only worry about excessive rebuilds if you havealready run the app in profile mode and verified that the performanceis not what you want.And remember, the widget rebuild information is only available ina debug build. Test the app’s performance on a real device in a profilebuild, but debug performance issues in a debug build.<topic_end><topic_start>Editing tips for Flutter codeIf you have additional tips we should share, let us know!<topic_end><topic_start>Assists & quick fixesAssists are code changes related to a certain code identifier.A number of these are available when the cursor is placed on aFlutter widget identifier, as indicated by the yellow lightbulb icon.The assist can be invoked by clicking the lightbulb, or by using thekeyboard shortcut (Alt+Enter on Linux and Windows,Option+Return on macOS), as illustrated here:Quick Fixes are similar, only they are shown with a piece of code has an errorand they can assist in correcting it. They are indicated with a red lightbulb.<topic_end><topic_start>Wrap with new widget assistThis can be used when you have a widget that you want to wrap in a surroundingwidget, for example if you want to wrap a widget in a Row or Column.<topic_end><topic_start>Wrap widget list with new widget assistSimilar to the assist above, but for wrapping an existing list ofwidgets rather than an individual widget.<topic_end><topic_start>Convert child to children assistChanges a child argument to a children argument,and wraps the argument value in a list.<topic_end><topic_start>Live templatesLive templates can be used to speed up entering typical code structures.They are invoked by typing their prefix, and then selecting it in the codecompletion window:The Flutter plugin includes the following templates:You can also define custom templates in Settings > Editor > Live Templates.<topic_end><topic_start>Keyboard shortcutsHot reloadOn Linux (keymap Default for XWin) and Windows the keyboard shortcutsare Control+Alt+; and Control+Backslash.On macOS (keymap Mac OS X 10.5+ copy) the keyboard shortcuts areCommand+Option and Command+Backslash.Keyboard mappings can be changed in the IDE Preferences/Settings: SelectKeymap, then enter flutter into the search box in the upper right corner.Right click the binding you want to change and Add Keyboard Shortcut.<topic_end><topic_start>Hot reload vs. hot restartHot reload works by injecting updated source code files into the runningDart VM (Virtual Machine). This includes not only adding new classes,but also adding methods and fields to existing classes,and changing existing functions.A few types of code changes cannot be hot reloaded though:For these changes you can fully restart your application,without having to end your debugging session. To perform a hot restart,don’t click the Stop button, simply re-click the Run button (if in a runsession) or Debug button (if in a debug session), or shift-click the ‘hotreload’ button.<topic_end><topic_start>Editing Android code in Android Studio with full IDE supportOpening the root directory of a Flutter project doesn’t expose all the Androidfiles to the IDE. Flutter apps contain a subdirectory named android. If youopen this subdirectory as its own separate project in Android Studio, the IDEwill be able to fully support editing and refactoring all Android files (likeGradle scripts).If you already have the entire project opened as a Flutter app in AndroidStudio, there are two equivalent ways to open the Android files on their ownfor editing in the IDE. Before trying this, make sure that you’re on the latestversion of Android Studio and the Flutter plugins.For both options, Android Studio gives you the option to use separate windows orto replace the existing window with the new project when opening a secondproject. Either option is fine.If you don’t already have the Flutter project opened in Android studio,you can open the Android files as their own project from the start:If you haven’t run your Flutter app yet, you might see Android Studio report abuild error when you open the android project. Run flutter pub get inthe app’s root directory and rebuild the project by selecting Build > Maketo fix it.<topic_end><topic_start>Editing Android code in IntelliJ IDEATo enable editing of Android code in IntelliJ IDEA, you need to configure thelocation of the Android SDK:<topic_end><topic_start>Tips and tricks<topic_end><topic_start>Troubleshooting<topic_end><topic_start>Known issues and feedbackImportant known issues that might impact your experience are documentedin the Flutter plugin README file.All known bugs are tracked in the issue trackers:We welcome feedback, both on bugs/issues and feature requests.Prior to filing new issues:When filing new issues, include the output of flutter doctor.<topic_end><topic_start>Visual Studio Code<topic_end><topic_start>Installation and setupFollow the Set up an editor instructions toinstall the Dart and Flutter extensions(also called plugins).<topic_end><topic_start>Updating the extensionUpdates to the extensions are shipped on a regular basis.By default, VS Code automatically updates extensions whenupdates are available.To install updates yourself:<topic_end><topic_start>Creating projectsThere are a couple ways to create a new project.<topic_end><topic_start>Creating a new projectTo create a new Flutter project from the Flutterstarter app template:Go to View >Command Palette….You can also press Ctrl / Cmd +Shift + P.<topic_end><topic_start>Opening a project from existing source codeTo open an existing Flutter project:Go to File > Open.You can also press Ctrl / Cmd + O<topic_end><topic_start>Editing code and viewing issuesThe Flutter extension performs code analysis.The code analysis can:Navigate to type declarationsFind type usages.View all current source code problems.<topic_end><topic_start>Running and debugginginfo Note  You can debug your app in a couple of ways.The instructions below describe features available in VS Code.  For information on using launching DevTools, see  Running DevTools from VS Code in the DevTools docs.Start debugging by clicking Run > Start Debuggingfrom the main IDE window, or press F5.<topic_end><topic_start>Selecting a target deviceWhen a Flutter project is open in VS Code,you should see a set of Flutter specific entries in the status bar,including a Flutter SDK version and adevice name (or the message No Devices):info NoteThe Flutter extension automatically selects the last device connected.However, if you have multiple devices/simulators connected, clickdevice in the status bar to see a pick-listat the top of the screen. Select the device you want to use forrunning or debugging.Are you developing for macOS or iOS remotely using  Visual Studio Code Remote? If so, you might need to manually  unlock the keychain. For more information, see this  question on StackExchange.<topic_end><topic_start>Run app without breakpointsGo to Run > Start Without Debugging.You can also press Ctrl + F5.<topic_end><topic_start>Run app with breakpointsClick Run > Start Debugging.You can also press F5.The status bar turns orange to show you are in a debug session.<topic_end><topic_start>Run app in debug, profile, or release modeFlutter offers many different build modes to run your app in. You can read more about them in Flutter’s build modes.Open the launch.json file in VS Code.If you don’t have a launch.json file:Go to View > Run.You can also press Ctrl / Cmd +Shift + DThe Run and Debug panel displays.Click create a launch.json file.In the configurations section,change the flutterMode property tothe build mode you want to target.For example, if you want to run in debug mode,your launch.json might look like this:Run the app through the Run panel.<topic_end><topic_start>Fast edit and refresh development cycleFlutter offers a best-in-class developer cycle enabling youto see the effect of your changes almost instantly with theStateful Hot Reload feature.To learn more, check out Hot reload.<topic_end><topic_start>Advanced debuggingYou might find the following advanced debugging tips useful:<topic_end><topic_start>Debugging visual layout issuesDuring a debug session,several additional debugging commands are added to theCommand Palette and to the Flutter inspector.When space is limited, the icon is used as the visualversion of the label.<topic_end><topic_start>Debugging external librariesBy default, debugging an external library is disabledin the Flutter extension. To enable:<topic_end><topic_start>Editing tips for Flutter codeIf you have additional tips we should share, let us know!<topic_end><topic_start>Assists & quick fixesAssists are code changes related to a certain code identifier.A number of these are available when the cursor is placed on aFlutter widget identifier, as indicated by the yellow lightbulb icon.To invoke the assist, click the lightbulb as shown in the following screenshot:You can also press Ctrl / Cmd + .Quick fixes are similar,only they are shown with a piece of code has an error and theycan assist in correcting it.<topic_end><topic_start>SnippetsSnippets can be used to speed up entering typical code structures.They are invoked by typing their prefix,and then selecting from the code completion window:The Flutter extension includes the following snippets:You can also define custom snippets by executingConfigure User Snippets from the Command Palette.<topic_end><topic_start>Keyboard shortcutsYou can also press Ctrl + F5(Cmd + F5  on macOS).Keyboard mappings can be changed by executing theOpen Keyboard Shortcuts command from the Command Palette.<topic_end><topic_start>Hot reload vs. hot restartHot reload works by injecting updated source code files into therunning Dart VM (Virtual Machine). This includes not onlyadding new classes, but also adding methods and fields toexisting classes, and changing existing functions.A few types of code changes cannot be hot reloaded though:For these changes, restart your app withoutending your debugging session. To perform a hot restart,run the Flutter: Hot Restart command from the Command Palette.You can also pressCtrl + Shift + F5or Cmd + Shift + F5 on macOS.<topic_end><topic_start>Troubleshooting<topic_end><topic_start>Known issues and feedbackAll known bugs are tracked in the issue tracker:Dart and Flutter extensions GitHub issue tracker.We welcome feedback,both on bugs/issues and feature requests.Prior to filing new issues:When filing new issues, include flutter doctor output.<topic_end><topic_start>DevTools<topic_end><topic_start>What is DevTools?DevTools is a suite of performance and debugging toolsfor Dart and Flutter.For a video introduction to DevTools, check outthe following deep dive and use case walkthrough:Dive in to DevTools<topic_end><topic_start>What can I do with DevTools?Here are some of the things you can do with DevTools:We expect you to use DevTools in conjunction withyour existing IDE or command-line based development workflow.<topic_end><topic_start>How to start DevToolsSee the VS Code, Android Studio/IntelliJ, orcommand line pages for instructions on how to start DevTools.<topic_end><topic_start>Troubleshooting some standard issuesQuestion: My app looks janky or stutters.  How do I fix it?Answer: Performance issues can cause UI frames  to be janky and/or slow down some operations.For more information, check out thePerformance page.Question: I see a lot of garbage collection (GC) events occurring.  Is this a problem?Answer: Frequent GC events might display on  the DevTools > Memory > Memory chart. In most cases,  it’s not a problem.If your app has frequent background activity with some idle time,Flutter might use that opportunity to collect the created objectswithout performance impact.<topic_end><topic_start>Providing feedbackPlease give DevTools a try, provide feedback, and file issuesin the DevTools issue tracker. Thanks!<topic_end><topic_start>Other resourcesFor more information on debugging and profilingFlutter apps, see the Debugging page and,in particular, its list of other resources.For more information on using DevTools with Dart command-line apps, see theDevTools documentation on dart.dev.<topic_end><topic_start>DevTools<topic_end><topic_start>What is DevTools?DevTools is a suite of performance and debugging toolsfor Dart and Flutter.For a video introduction to DevTools, check outthe following deep dive and use case walkthrough:Dive in to DevTools<topic_end><topic_start>What can I do with DevTools?Here are some of the things you can do with DevTools:We expect you to use DevTools in conjunction withyour existing IDE or command-line based development workflow.<topic_end><topic_start>How to start DevToolsSee the VS Code, Android Studio/IntelliJ, orcommand line pages for instructions on how to start DevTools.<topic_end><topic_start>Troubleshooting some standard issuesQuestion: My app looks janky or stutters.  How do I fix it?Answer: Performance issues can cause UI frames  to be janky and/or slow down some operations.For more information, check out thePerformance page.Question: I see a lot of garbage collection (GC) events occurring.  Is this a problem?Answer: Frequent GC events might display on  the DevTools > Memory > Memory chart. In most cases,  it’s not a problem.If your app has frequent background activity with some idle time,Flutter might use that opportunity to collect the created objectswithout performance impact.<topic_end><topic_start>Providing feedbackPlease give DevTools a try, provide feedback, and file issuesin the DevTools issue tracker. Thanks!<topic_end><topic_start>Other resourcesFor more information on debugging and profilingFlutter apps, see the Debugging page and,in particular, its list of other resources.For more information on using DevTools with Dart command-line apps, see theDevTools documentation on dart.dev.<topic_end><topic_start>Install and run DevTools from Android Studio<topic_end><topic_start>Install the Flutter pluginInstall the Flutter plugin if you don’t already have it installed.This can be done using the normal Plugins page in the IntelliJand Android Studio settings. Once that page is open,you can search the marketplace for the Flutter plugin.<topic_end><topic_start>Start an app to debugTo open DevTools, you first need to run a Flutter app.This can be accomplished by opening a Flutter project,ensuring that you have a device connected,and clicking the Run or Debug toolbar buttons.<topic_end><topic_start>Launch DevTools from the toolbar/menuOnce an app is running,you can start DevTools using one of the following:<topic_end><topic_start>Launch DevTools from an actionYou can also open DevTools from an IntelliJ action.Open the Find Action… dialog(on macOS, press Cmd + Shift + A),and search for the Open DevTools action.When you select that action,DevTools is installed (if it isn’t already), the DevTools serverlaunches, and a browser instance opens pointing to the DevTools app.When opened with an IntelliJ action, DevTools is not connectedto a Flutter app. You’ll need to provide a service protocol portfor a currently running app. You can do this using the inlineConnect to a running app dialog.<topic_end><topic_start>Install and run DevTools from VS Code<topic_end><topic_start>Install the VS Code extensionsTo use the DevTools from VS Code, you need the Dart extension.If you’re debugging Flutter applications, you should also installthe Flutter extension.<topic_end><topic_start>Start an application to debugStart a debug session for your application by opening the rootfolder of your project (the one containing pubspec.yaml)in VS Code and clicking Run > Start Debugging (F5).<topic_end><topic_start>Launch DevToolsOnce the debug session is active and the application has started,the Open DevTools commands become available in theVS Code command palette (F1):The chosen tool will be opened embedded inside VS Code.You can choose to have DevTools always opened in a browser with thedart.embedDevTools setting, and control whether it opens as a full window orin a new column next to your current editor with the dart.devToolsLocationsetting.A full list of Dart/Flutter settings are availablehere or in theVS Code settings editor.Some recommendation settings for Dart/Flutter in VS Code can be foundhere.You can also see whether DevTools is running and launch it in a browser from thelanguage status area (the {} icon next to Dart in the status bar).<topic_end><topic_start>Install and run DevTools from the command lineTo run Dart DevTools from the CLI, you must have dart on your path. Thenyou can run the following command to launch DevTools:To upgrade DevTools, upgrade your Dart SDK. If a newer Dart SDKincludes a newer version of DevTools, dart devtools will automaticallylaunch this version. If which dart points to the Dart SDK included inyour Flutter SDK, then DevTools will be upgraded when you upgrade yourFlutter SDK to a newer version.When you run DevTools from the command line, you should see output thatlooks something like:<topic_end><topic_start>Start an application to debugNext, start an app to connect to.This can be either a Flutter applicationor a Dart command-line application.The command below specifies a Flutter app:You need to have a device connected, or a simulator open,for flutter run to work. Once the app starts, you’ll see amessage in your terminal that looks like the following:Open the DevTools instance connected to your appby opening the second link in Chrome.This URL contains a security token, so it’s different for each run of your app. This means that if you stop your application and re-run it, you need to connect to DevTools again with the new URL.<topic_end><topic_start>Connect to a new app instanceIf your app stops runningor you opened DevTools manually,you should see a Connect dialog:You can manually connect DevTools to a new app instanceby copying the Observatory link you got from running your app,such as http://127.0.0.1:52129/QjqebSY4lQ8=/and pasting it into the connect dialog:<topic_end><topic_start>Using the Flutter inspectorinfo Note  The inspector works with all Flutter applications.<topic_end><topic_start>What is it?The Flutter widget inspector is a powerful tool for visualizing andexploring Flutter widget trees. The Flutter framework uses widgetsas the core building block for anything from controls(such as text, buttons, and toggles),to layout (such as centering, padding, rows, and columns).The inspector helps you visualize and explore Flutter widgettrees, and can be used for the following:<topic_end><topic_start>Get startedTo debug a layout issue, run the app in debug mode andopen the inspector by clicking the Flutter Inspectortab on the DevTools toolbar.info Note  You can still access the Flutter inspector directly from  Android Studio/IntelliJ, but you might prefer the  more spacious view when running it from DevTools  in a browser.<topic_end><topic_start>Debugging layout issues visuallyThe following is a guide to the features available in theinspector’s toolbar. When space is limited, the icon isused as the visual version of the label.<topic_end><topic_start>Inspecting a widgetYou can browse the interactive widget tree to view nearbywidgets and see their field values.To locate individual UI elements in the widget tree,click the Select Widget Mode button in the toolbar.This puts the app on the device into a “widget select” mode.Click any widget in the app’s UI; this selects the widget on theapp’s screen, and scrolls the widget tree to the corresponding node.Toggle the Select Widget Mode button again to exitwidget select mode.When debugging layout issues, the key fields to look at are thesize and constraints fields. The constraints flow down the tree,and the sizes flow back up. For more information on how this works,see Understanding constraints.<topic_end><topic_start>Flutter Layout ExplorerThe Flutter Layout Explorer helps you to better understandFlutter layouts.For an overview of what you can do with this tool, seethe Flutter Explorer video:You might also find the following step-by-step article useful:<topic_end><topic_start>Using the Layout ExplorerFrom the Flutter Inspector, select a widget. The Layout Explorersupports both flex layouts and fixed size layouts, and hasspecific tooling for both kinds.<topic_end><topic_start>Flex layoutsWhen you select a flex widget (for example, Row, Column, Flex)or a direct child of a flex widget, the flex layout tool willappear in the Layout Explorer.The Layout Explorer visualizes how Flex widgets and theirchildren are laid out. The explorer identifies the main axisand cross axis, as well as the current alignment for each(for example, start, end, and spaceBetween).It also shows details like flex factor, flex fit, and layoutconstraints.Additionally, the explorer shows layout constraint violationsand render overflow errors. Violated layout constraintsare colored red, and overflow errors are presented in thestandard  “yellow-tape” pattern, as you might see on a runningdevice. These visualizations aim to improve understanding ofwhy overflow errors occur as well as how to fix them.Clicking a widget in the layout explorer mirrorsthe selection on the on-device inspector. Select Widget Modeneeds to be enabled for this. To enable it,click on the Select Widget Mode button in the inspector.For some properties, like flex factor, flex fit, and alignment,you can modify the value via dropdown lists in the explorer.When modifying a widget property, you see the new value reflectednot only in the Layout Explorer, but also on thedevice running your Flutter app. The explorer animateson property changes so that the effect of the change is clear.Widget property changes made from the layout explorer don’tmodify your source code and are reverted on hot reload.<topic_end><topic_start>Interactive PropertiesLayout Explorer supports modifying mainAxisAlignment,crossAxisAlignment, and FlexParentData.flex.In the future, we may add support for additional propertiessuch as mainAxisSize, textDirection, andFlexParentData.fit.<topic_end><topic_start>mainAxisAlignmentSupported values:<topic_end><topic_start>crossAxisAlignmentSupported values:<topic_end><topic_start>FlexParentData.flexLayout Explorer supports 7 flex options in the UI(null, 0, 1, 2, 3, 4, 5), but technically the flexfactor of a flex widget’s child can be any int.<topic_end><topic_start>Flexible.fitLayout Explorer supports the two different types ofFlexFit: loose and tight.<topic_end><topic_start>Fixed size layoutsWhen you select a fixed size widget that is not a childof a flex widget, fixed size layout information will appearin the Layout Explorer. You can see size, constraint, and paddinginformation for both the selected widget and its nearest upstreamRenderObject.<topic_end><topic_start>Visual debuggingThe Flutter Inspector provides several options for visually debugging your app.<topic_end><topic_start>Slow animationsWhen enabled, this option runs animations 5 times slower for easier visualinspection.This can be useful if you want to carefully observe and tweak an animation thatdoesn’t look quite right.This can also be set in code:<code_start>import 'package:flutter/scheduler.dart';void setSlowAnimations() {  timeDilation = 5.0;}<code_end>This slows the animations by 5x.<topic_end><topic_start>See alsoThe following links provide more info.The following screen recordings show before and after slowing an animation.<topic_end><topic_start>Show guidelinesThis feature draws guidelines over your app that display render boxes, alignments,paddings, scroll views, clippings and spacers.This tool can be used for better understanding your layout. For instance,by finding unwanted padding or understanding widget alignment.You can also enable this in code:<code_start>import 'package:flutter/rendering.dart';void showLayoutGuidelines() {  debugPaintSizeEnabled = true;}<code_end><topic_end><topic_start>Render boxesWidgets that draw to the screen create a render box, thebuilding blocks of Flutter layouts. They’re shown with a bright blue border:<topic_end><topic_start>AlignmentsAlignments are shown with yellow arrows. These arrows show the verticaland horizontal offsets of a widget relative to its parent.For example, this button’s icon is shown as being centered by the four arrows:<topic_end><topic_start>PaddingPadding is shown with a semi-transparent blue background:<topic_end><topic_start>Scroll viewsWidgets with scrolling contents (such as list views) are shown with green arrows:<topic_end><topic_start>ClippingClipping, for example when using the ClipRect widget, are shownwith a dashed pink line with a scissors icon:<topic_end><topic_start>SpacersSpacer widgets are shown with a grey background,such as this SizedBox without a child:<topic_end><topic_start>Show baselinesThis option makes all baselines visible.Baselines are horizontal lines used to position text.This can be useful for checking whether text is precisely aligned vertically.For example, the text baselines in the following screenshot are slightly misaligned:The Baseline widget can be used to adjust baselines.A line is drawn on any render box that has a baseline set;alphabetic baselines are shown as green and ideographic as yellow.You can also enable this in code:<code_start>import 'package:flutter/rendering.dart';void showBaselines() {  debugPaintBaselinesEnabled = true;}<code_end><topic_end><topic_start>Highlight repaintsThis option draws a border around all render boxesthat changes color every time that box repaints.This rotating rainbow of colors is useful for finding parts of your appthat are repainting too often and potentially harming performance.For example, one small animation could be causing an entire pageto repaint on every frame.Wrapping the animation in a RepaintBoundary widget limitsthe repainting to just the animation.Here the progress indicator causes its container to repaint:<code_start>class EverythingRepaintsPage extends StatelessWidget {  const EverythingRepaintsPage({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Repaint Example')),      body: const Center(        child: CircularProgressIndicator(),      ),    );  }}<code_end>Wrapping the progress indicator in a RepaintBoundary causesonly that section of the screen to repaint:<code_start>class AreaRepaintsPage extends StatelessWidget {  const AreaRepaintsPage({super.key});  @override  Widget build(BuildContext context) {    return Scaffold(      appBar: AppBar(title: const Text('Repaint Example')),      body: const Center(        child: RepaintBoundary(          child: CircularProgressIndicator(),        ),      ),    );  }}<code_end>RepaintBoundary widgets have tradeoffs. They can help with performance,but they also have an overhead of creating a new canvas,which uses additional memory.You can also enable this option in code:<code_start>import 'package:flutter/rendering.dart';void highlightRepaints() {  debugRepaintRainbowEnabled = true;}<code_end><topic_end><topic_start>Highlight oversized imagesThis option highlights images that are too large by both inverting their colorsand flipping them vertically:The highlighted images use more memory than is required;for example, a large 5MB image displayed at 100 by 100 pixels.Such images can cause poor performance, especially on lower-end devicesand when you have many images, as in a list view,this performance hit can add up.Information about each image is printed in the debug console:Images are deemed too large if they use at least 128KB more than required.<topic_end><topic_start>Fixing imagesWherever possible, the best way to fix this problem is resizingthe image asset file so it’s smaller.If this isn’t possible, you can use the cacheHeight and cacheWidthparameters on the Image constructor:<code_start>class ResizedImage extends StatelessWidget {  const ResizedImage({super.key});  @override  Widget build(BuildContext context) {    return Image.asset(      'dash.png',      cacheHeight: 213,      cacheWidth: 392,    );  }}<code_end>This makes the engine decode this image at the specified size,and reduces memory usage (decoding and storage is still more expensivethan if the image asset itself was shrunk).The image is rendered to the constraints of the layout or width and heightregardless of these parameters.This property can also be set in code:<code_start>void showOversizedImages() {  debugInvertOversizedImages = true;}<code_end><topic_end><topic_start>More informationYou can learn more at the following link:<topic_end><topic_start>Details TreeSelect the Widget Details Tree tab to display the details tree for theselected widget. From here, you can gather useful information about awidget’s properties, render object, and children.<topic_end><topic_start>Track widget creationPart of the functionality of the Flutter inspector is based oninstrumenting the application code in order to better understandthe source locations where widgets are created. The sourceinstrumentation allows the Flutter inspector to present thewidget tree in a manner similar to how the UI was definedin your source code. Without it, the tree of nodes in thewidget tree are much deeper, and it can be more difficult tounderstand how the runtime widget hierarchy corresponds toyour application’s UI.You can disable this feature by passing --no-track-widget-creation tothe flutter run command.Here are examples of what your widget tree might look likewith and without track widget creation enabled.Track widget creation enabled (default):Track widget creation disabled (not recommended):This feature prevents otherwise-identical const Widgets frombeing considered equal in debug builds. For more details, seethe discussion on common problems when debugging.<topic_end><topic_start>Inspector settings<topic_end><topic_start>Enable hover inspectionHovering over any widget displays its properties and values.Toggling this value enables or disables the hover inspection functionality.<topic_end><topic_start>Package directoriesBy default, DevTools limits the widgets displayed in the widget treeto those from the project’s root directory, and those from Flutter. Thisfiltering only applies to the widgets in the Inspector Widget Tree (left sideof the Inspector) – not the Widget Details Tree (right side of the Inspectorin the same tab view as the Layout Explorer). In the Widget Details Tree, youwill be able to see all widgets in the tree from all packages.In order to show other widgets, a parent directory of theirs must be added to the Package Directories.For example, consider the following directory structure:Running your app from project_foo_app displays only widgets fromproject_foo/pkgs/project_foo_app in the widget inspector tree.To show widgets from widgets_A in the widget tree,add project_foo/pkgs/widgets_A to the package directories.To display all widgets from your project root in the widget tree,add project_foo to the package directories.Changes to your package directories persist the next time thewidget inspector is opened for the app.<topic_end><topic_start>Other resourcesFor a demonstration of what’s generally possible with the inspector,see the DartConf 2018 talk demonstrating the IntelliJ versionof the Flutter inspector.To learn how to visually debug layout issuesusing DevTools, check out a guidedFlutter Inspector tutorial.<topic_end><topic_start>Using the Performance viewinfo Note  The DevTools performance view works for Flutter mobile and desktop apps.  For web apps, Flutter adds timeline events to the  performance panel of Chrome DevTools instead.  To learn about profiling web apps, check out Debugging web performance.The performance page can help you diagnose performanceproblems and UI jank in your application.This page offers timing and performance informationfor activity in your application.It consists of several tools to help you identifythe cause of poor performance in your app:Use a profile build of your application to analyze performance.  Frame rendering times aren’t indicative of release performance  when running in debug mode. Run your app in profile mode,  which still preserves useful debugging information.The performance view also supports importing and exporting ofdata snapshots. For more information,check out the Import and export section.<topic_end><topic_start>What is a frame in Flutter?Flutter is designed to render its UI at 60 frames per second(fps), or 120 fps on devices capable of 120Hz updates.Each render is called a frame.This means that, approximately every 16ms, the UI updatesto reflect animations or other changes to the UI. A framethat takes longer than 16ms to render causes jank(jerky motion) on the display device.<topic_end><topic_start>Flutter frames chartThis chart contains Flutter frame information for your application.Each bar set in the chart represents a single Flutter frame.The bars are color-coded to highlight the different portionsof work that occur when rendering a Flutter frame: work fromthe UI thread and work from the raster thread.This chart contains Flutter frame timing information for yourapplication. Each pair of bars in the chart represents a singleFlutter frame. Selecting a frame from this chart updates the datathat is displayed below in the Frame analysis tabor the Timeline events tab.(As of DevTools 2.23.1, the Raster statsis a standalone feature without data per frame).The flutter frames chart updates when new framesare drawn in your app. To pause updates to this chart,click the pause button to the right of the chart.This chart can be collapsed to provide more viewing spacefor data below by clicking the Flutter frames button above the chart.The pair of bars representing each Flutter frame are color-codedto highlight the different portions of work that occur when renderinga Flutter frame: work from the UI thread and work from the raster thread.<topic_end><topic_start>UIThe UI thread executes Dart code in the Dart VM. This includescode from your application as well as the Flutter framework.When your app creates and displays a scene, the UI thread createsa layer tree, a lightweight object containing device-agnosticpainting commands, and sends the layer tree to the raster threadto be rendered on the device. Do not block this thread.<topic_end><topic_start>RasterThe raster thread executes graphics code from the Flutter Engine.This thread takes the layer tree and displays it by talking tothe GPU (graphic processing unit). You can’t directly accessthe raster thread or its data, but if this thread is slow,it’s a result of something you’ve done in the Dart code.Skia, the graphics library, runs on this thread.Impeller also uses this thread.Sometimes a scene results in a layer tree that is easy to construct,but expensive to render on the raster thread. In this case, youneed to figure out what your code is doing that is causingrendering code to be slow. Specific kinds of workloads are moredifficult for the GPU. They might involve unnecessary calls tosaveLayer(), intersecting opacities with multiple objects,and clips or shadows in specific situations.For more information on profiling, check outIdentifying problems in the GPU graph.<topic_end><topic_start>Jank (slow frame)The frame rendering chart shows jank with a red overlay.A frame is considered to be janky if it takes more than~16 ms to complete (for 60 FPS devices). To achieve a frame rendering rate of60 FPS (frames per second), each frame must render in~16 ms or less. When this target is missed, you mayexperience UI jank or dropped frames.For more information on how to analyze your app’s performance,check out Flutter performance profiling.<topic_end><topic_start>Shader compilationShader compilation occurs when a shader is first used in your Flutterapp. Frames that perform shader compilation are marked in darkred:For more information on how to reduce shader compilation jank,check out Reduce shader compilation jank on mobile.<topic_end><topic_start>Frame analysis tabSelecting a janky frame (slow, colored in red)from the Flutter frames chart above shows debugging hintsin the Frame analysis tab. These hints help you diagnosejank in your app, and notify you of any expensive operationsthat we have detected that might have contributed to the slow frame time.<topic_end><topic_start>Raster stats tabinfo Note  For best results, this tool should be used with  the Impeller rendering engine. When using Skia,  the raster stats reported might be inconsistent  due to the timing of when shaders are compiled.If you have Flutter frames that are janking withslow raster thread times, this tool might be ableto help you diagnose the source of the slow performance.To generate raster stats:If you see an expensive layer, find the Dart code in your appthat is producing this layer and investigate further.You can make changes to your code, hot reload,and take new snapshots to see if the performance of a layerwas improved by your change.<topic_end><topic_start>Timeline events tabThe timeline events chart shows all event tracing from your application.The Flutter framework emits timeline events as it works to build frames,draw scenes, and track other activity such as HTTP request timingsand garbage collection. These events show up here in the Timeline.You can also send your own Timeline events using the dart:developerTimeline and TimelineTask APIs.For help with navigating and using the trace viewer,click the ? button at the top right of the timelineevents tab bar. To refresh the timeline with new events fromyour application, click the refresh button(also in the upper right corner of the tab controls).<topic_end><topic_start>Advanced debugging tools<topic_end><topic_start>Enhance tracingTo view more detailed tracing in the timeline events chart,use the options in the enhance tracing dropdown:info Note  Frame times might be negatively affected when these options are enabled.To see the new timeline events, reproduce the activityin your app that you are interested in tracing,and then select a frame to inspect the timeline.<topic_end><topic_start>Track widget buildsTo see the build() method events in the timeline,enable the Track Widget Builds option.The name of the widget is shown in the timeline event.Watch this video for an example of tracking widget builds<topic_end><topic_start>Track layoutsTo see render object layout events in the timeline,enable the Track Layouts option:Watch this video for an example of tracking layouts<topic_end><topic_start>Track paintsTo see render object paint events in the timeline,enable the Track Paints option:Watch this video for an example of tracking paints<topic_end><topic_start>More debugging optionsTo diagnose performance problems related to rendering layers,toggle off a rendering layer.These options are enabled by default.To see the effects on your app’s performance,reproduce the activity in your app.Then select the new frames in the frames chartto inspect the timeline eventswith the layers disabled.If Raster time has significantly decreased,excessive use of the effects you disabled might be contributingto the jank you saw in your app.<topic_end><topic_start>Import and exportDevTools supports importing and exporting performance snapshots.Clicking the export button (upper-right corner above theframe rendering chart) downloads a snapshot of the current data on theperformance page. To import a performance snapshot, you can drag and drop thesnapshot into DevTools from any page. Note that DevTools onlysupports importing files that were originally exported from DevTools.<topic_end><topic_start>Other resourcesTo learn how to monitor an app’s performance anddetect jank using DevTools, check out a guidedPerformance View tutorial.<topic_end><topic_start>Using the CPU profiler viewinfo Note  The CPU profiler view works with Dart CLI and mobile apps only.  Use Chrome DevTools to analyze performance  of a web app.The CPU profiler view allows you to record and profile asession from your Dart or Flutter application.The profiler can help you solve performance problemsor generally understand your app’s CPU activity.The Dart VM collects CPU samples(a snapshot of the CPU call stack at a single point in time)and sends the data to DevTools for visualization.By aggregating many CPU samples together,the profiler can help you understand where the CPUspends most of its time.info NoteIf you are running a Flutter application,  use a profile build to analyze performance.  CPU profiles are not indicative of release performance  unless your Flutter application is run in profile mode.<topic_end><topic_start>CPU profilerStart recording a CPU profile by clicking Record.When you are done recording, click Stop. At this point,CPU profiling data is pulled from the VM and displayedin the profiler views (Call tree, Bottom up, Method table,and Flame chart).To load all available CPU samples without manuallyrecording and stopping, you can click Load all CPU samples,which pulls all CPU samples that the VM has recorded andstored in its ring buffer, and then displays thoseCPU samples in the profiler views.<topic_end><topic_start>Bottom upThis table provides a bottom-up representationof a CPU profile. This means that each top-level method,or root, in the bottom up table is actually thetop method in the call stack for one or more CPU samples.In other words, each top-level method in a bottom uptable is a leaf node from the top down table(the call tree).In this table, a method can be expanded to show its callers.This view is useful for identifying expensive methodsin a CPU profile. When a root node in this tablehas a high self time, that means that many CPU samplesin this profile ended with that method on top of the call stack.See the Guidelines section below to learn how toenable the blue and green vertical lines seen in this image.Tooltips can help you understand the values in each column:Table element (self time)<topic_end><topic_start>Call treeThis table provides a top-down representation of a CPU profile.This means that each top-level method in the call tree is a rootof one or more CPU samples. In this table,a method can be expanded to show its callees.This view is useful for identifying expensive paths in a CPU profile.When a root node in this table has a high total time,that means that many CPU samples in this profile startedwith that method on the bottom of the call stack.See the Guidelines section below to learn how toenable the blue and green vertical lines seen in this image.Tooltips can help you understand the values in each column:<topic_end><topic_start>Method tableThe method table provides CPU statistics for each methodcontained in a CPU profile. In the table on the left,all available methods are listed with their total andself time.Total time is the combined time that a method spentanywhere on the call stack, or in other words,the time a method spent executing its own code andany code for methods that it called.Self time is the combined time that a method spenton top of the call stack, or in other words,the time a method spent executing only its own code.Selecting a method from the table on the left showsthe call graph for that method. The call graph showsa method’s callers and callees and their respectivecaller / callee percentages.<topic_end><topic_start>Flame chartThe flame chart view is a graphical representation ofthe Call tree. This is a top-down viewof a CPU profile, so in this chart,the top-most method calls the one below it.The width of each flame chart element represents theamount of time that a method spent on the call stack.Like the Call tree, this view is useful for identifyingexpensive paths in a CPU profile.The help menu, which can be opened by clicking the ? iconnext to the search bar, provides information about how tonavigate and zoom within the chart and a color-coded legend.<topic_end><topic_start>CPU sampling rateDevTools sets a default rate at which the VM collects CPU samples:1 sample / 250 μs (microseconds). This is selected by default onthe CPU profiler page as “Cpu sampling rate: medium”.This rate can be modified using the selector at the topof the page.The low, medium, and high sampling rates are1,000 Hz, 4,000 Hz, and 20,000 Hz, respectively.It’s important to know the trade-offsof modifying this setting.A profile that was recorded with a higher sampling rateyields a more fine-grained CPU profile with more samples.This might affect performance of your app since the VMis being interrupted more often to collect samples.This also causes the VM’s CPU sample buffer to overflow more quickly.The VM has limited space where it can store CPU sample information.At a higher sampling rate, the space fills up and beginsto overflow sooner than it would have if a lower samplingrate was used.This means that you might not have access to CPU samplesfrom the beginning of the recorded profile, dependingon whether the buffer overflows during the time of recording.A profile that was recorded with a lower sampling rateyields a more coarse-grained CPU profile with fewer samples.This affects your app’s performance less,but you might have access to less information about whatthe CPU was doing during the time of the profile.The VM’s sample buffer also fills more slowly, so you can seeCPU samples for a longer period of app run time.This means that you have a better chance of viewing CPUsamples from the beginning of the recorded profile.<topic_end><topic_start>FilteringWhen viewing a CPU profile, you can filter the data bylibrary, method name, or UserTag.<topic_end><topic_start>GuidelinesWhen looking at a call tree or bottom up view,sometimes the trees can be very deep.To help with viewing parent-child relationships in a deep tree,enable the Display guidelines option.This adds vertical guidelines between parent and child in the tree.<topic_end><topic_start>Other resourcesTo learn how to use DevTools to analyzethe CPU usage of a compute-intensive Mandelbrot app,check out a guided CPU Profiler View tutorial.Also, learn how to analyze CPU usage when the appuses isolates for parallel computing.<topic_end><topic_start>Using the Memory viewThe memory view provides insights into detailsof the application’s memory allocation andtools to detect and debug specific issues.info Note  This page is up to date for DevTools 2.23.0.For information on how to locate DevTools screens in different IDEs,check out the DevTools overview.To better understand the insights found on this page,the first section explains how Dart manages memory.If you already understand Dart’s memory management,you can skip to the Memory view guide.<topic_end><topic_start>Reasons to use the memory viewUse the memory view for preemptive memory optimization or whenyour application experiences one of the following conditions:<topic_end><topic_start>Basic memory conceptsDart objects created using a class constructor(for example, by using MyClass()) live in aportion of memory called the heap. The memoryin the heap is managed by the Dart VM (virtual machine).The Dart VM allocates memory for the object at the moment of the object creation,and releases (or deallocates) the memory when the objectis no longer used (see Dart garbage collection).<topic_end><topic_start>Object types<topic_end><topic_start>Disposable objectA disposable object is any Dart object that defines a dispose() method.To avoid memory leaks, invoke dispose when the object isn’t needed anymore.<topic_end><topic_start>Memory-risky objectA memory-risky object is an object that might cause a memory leak,if it is not disposed properly or disposed but not GCed.<topic_end><topic_start>Root object, retaining path, and reachability<topic_end><topic_start>Root objectEvery Dart application creates a root object that references,directly or indirectly, all other objects the application allocates.<topic_end><topic_start>ReachabilityIf, at some moment of the application run,the root object stops referencing an allocated object,the object becomes unreachable,which is a signal for the garbage collector (GC)to deallocate the object’s memory.<topic_end><topic_start>Retaining pathThe sequence of references from root to an objectis called the object’s retaining path,as it retains the object’s memory from the garbage collection.One object can have many retaining paths.Objects with at least one retaining path arecalled reachable objects.<topic_end><topic_start>ExampleThe following example illustrates the concepts:<topic_end><topic_start>Shallow size vs retained sizeShallow size includes only the size of the objectand its references, while retained size also includesthe size of the retained objects.The retained size of the root object includesall reachable Dart objects.In the following example, the size of myHugeInstanceisn’t part of the parent’s or child’s shallow sizes,but is part of their retained sizes:In DevTools calculations, if an object has morethan one retaining path, its size is assigned asretained only to the members of the shortest retaining path.In this example the object x has two retaining paths:Only members of the shortest path (d and e) will includex into their retaining size.<topic_end><topic_start>Memory leaks happen in Dart?Garbage collector cannot prevent all types of memory leaks, and developersstill need to watch objects to have leak-free lifecycle.<topic_end><topic_start>Why can’t the garbage collector prevent all leaks?While the garbage collector takes care of allunreachable objects, it’s the responsibilityof the application to ensure that unneeded objectsare no longer reachable (referenced from the root).So, if non-needed objects are left referenced(in a global or static variable,or as a field of a long-living object),the garbage collector can’t recognize them,the memory allocation grows progressively,and the app eventually crashes with an out-of-memory error.<topic_end><topic_start>Why closures require extra attentionOne hard-to-catch leak pattern relates to using closures.In the following code, a reference to thedesigned-to-be short-living myHugeObject is implicitlystored in the closure context and passed to setHandler.As a result, myHugeObject won’t be garbage collectedas long as handler is reachable.<topic_end><topic_start>Why BuildContext requires extra attentionAn example of a large, short-living object thatmight squeeze into a long-living area and thus cause leaks,is the context parameter passed to Flutter’sbuild method.The following code is leak prone,as useHandler might store the handlerin a long-living area:<topic_end><topic_start>How to fix leak prone code?The following code is not leak prone,because:<topic_end><topic_start>General rule for BuildContextIn general, use the following rule for aBuildContext: if the closure doesn’t outlivethe widget, it’s ok to pass the context to the closure.Stateful widgets require extra attention.They consist of two classes: the widget and thewidget state,where the widget is short living,and the state is long living. The build context,owned by the widget, should never be referencedfrom the state’s fields, as the state won’t be garbagecollected together with the widget, and can significantly outlive it.<topic_end><topic_start>Memory leak vs memory bloatIn a memory leak, an application progressively uses memory,for example, by repeatedly creating a listener,but not disposing it.Memory bloat uses more memory than is necessary foroptimal performance, for example, by using overly largeimages or keeping streams open through their lifetime.Both leaks and bloats, when large,cause an application to crash with an out-of-memory error.However, leaks are more likely to cause memory issues,because even a small leak,if repeated many times, leads to a crash.<topic_end><topic_start>Memory view guideThe DevTools memory view helps you investigatememory allocations (both in the heap and external),memory leaks, memory bloat, and more. The viewhas the following features:<topic_end><topic_start>Expandable chartThe expandable chart provides the following features:<topic_end><topic_start>Memory anatomyA timeseries graph visualizes the state ofFlutter memory at successive intervals of time.Each data point on the chart corresponds to thetimestamp (x-axis) of measured quantities (y-axis)of the heap. For example, usage, capacity, external,garbage collection, and resident set size are captured.<topic_end><topic_start>Memory overview chartThe memory overview chart is a timeseries graphof collected memory statistics. It visually presentsthe state of the Dart or Flutter heap and Dart’sor Flutter’s native memory over time.The chart’s x-axis is a timeline of events (timeseries).The data plotted in the y-axis all has a timestamp ofwhen the data was collected. In other words,it shows the polled state (capacity, used, external,RSS (resident set size), and GC (garbage collection))of the memory every 500 ms. This helps provide a liveappearance on the state of the memory as the application is running.Clicking the Legend button displays thecollected measurements, symbols, and colorsused to display the data.The Memory Size Scale y-axis automaticallyadjusts to the range of data collected in thecurrent visible chart range.The quantities plotted on the y-axis are as follows:<topic_end><topic_start>Profile Memory tabUse the Profile Memory tab to see current memoryallocation by class and memory type. For adeeper analysis in Google Sheets or other tools,download the data in CSV format.Toggle Refresh on GC, to see allocation in real time.<topic_end><topic_start>Diff Snapshots tabUse the Diff Snapshots tab to investigate a feature’smemory management. Follow the guidance on the tabto take snapshots before and after interactionwith the application, and diff the snapshots:Tap the Filter classes and packages button,to narrow the data:For a deeper analysis in Google Sheetsor other tools, download the data in CSV format.<topic_end><topic_start>Trace Instances tabUse the Trace Instances tab to investigate what methodsallocate memory for a set of classes during feature execution:<topic_end><topic_start>Bottom up vs call tree viewSwitch between bottom-up and call tree viewsdepending on specifics of your tasks.The call tree view shows the method allocationsfor each instance. The view is a top-down representationof the call stack, meaning that a method can be expandedto show its callees.The bottom-up view shows the list of differentcall stacks that have allocated the instances.<topic_end><topic_start>Other resourcesFor more information, check out the following resources:<topic_end><topic_start>Using the Debug consoleThe DevTools Debug console allows you to watch anapplication’s standard output (stdout),evaluate expressions for a paused or runningapp in debug mode, and analyze inbound and outboundreferences for objects.info Note  This page is up to date for DevTools 2.23.0.The Debug console is available from the Inspector,Debugger, and Memory views.<topic_end><topic_start>Watch application outputThe console shows the application’s standard output (stdout):<topic_end><topic_start>Explore inspected widgetsIf you click a widget on the Inspector screen,the variable for this widget displays in the Console:<topic_end><topic_start>Evaluate expressionsIn the console, you can evaluate expressions for a pausedor running application, assuming that you are runningyour app in debug mode:To assign an evaluated object to a variable,use $0, $1 (through $5) in the form of var x = $0:<topic_end><topic_start>Browse heap snapshotTo drop a variable to the console from a heap snapshot,do the following:The Console screen displays both live and staticinbound and outbound references, as well as field values:<topic_end><topic_start>Using the Network Viewinfo Note  The network view works with all Flutter and Dart applications.<topic_end><topic_start>What is it?The network view allows you to inspect HTTP, HTTPS, and web socket traffic fromyour Dart or Flutter application.<topic_end><topic_start>How to use itNetwork traffic should be recording by default when you open the Network page.If it is not, click the Resume button in the upper left tobegin polling.Select a network request from the table (left) to view details (right). You caninspect general and timing information about the request, as well as the contentof response and request headers and bodies.<topic_end><topic_start>Search and filteringYou can use the search and filter controls to find a specific request or filterrequests out of the request table.To apply a filter, press the filter button (right of the search bar). You willsee a filter dialog pop up:The filter query syntax is described in the dialog. You can filter networkrequests by the following keys:Any text that is not paired with an available filter key will be queried againstall categories (method, uri, status, type).Example filter queries:<topic_end><topic_start>Other resourcesHTTP and HTTPs requests are also surfaced in the Timeline asasynchronous timeline events. Viewing network activity in the timeline can beuseful if you want to see how HTTP traffic aligns with other events happeningin your app or in the Flutter framework.To learn how to monitor an app’s network traffic and inspectdifferent types of requests using the DevTools,check out a guided Network View tutorial.The tutorial also uses the view to identify network activity thatcauses poor app performance.<topic_end><topic_start>Using the debuggerinfo Note  DevTools hides the Debugger tab if the app was launched  from VS Code because VS Code has a built-in debugger.<topic_end><topic_start>Getting startedDevTools includes a full source-level debugger, supportingbreakpoints, stepping, and variable inspection.info Note  The debugger works with all Flutter and Dart applications.  If you are looking for a way to use GDB to remotely debug the  Flutter engine running within an Android app process,  check out flutter_gdb.When you open the debugger tab, you should see the source for the mainentry-point for your app loaded in the debugger.In order to browse around more of your application sources, click Libraries(top right) or press Ctrl / Cmd + P.This opens the libraries window and allows youto search for other source files.<topic_end><topic_start>Setting breakpointsTo set a breakpoint, click the left margin (the line number ruler)in the source area. Clicking once sets a breakpoint, which shouldalso show up in the Breakpoints area on the left. Clickingagain removes the breakpoint.<topic_end><topic_start>The call stack and variable areasWhen your application encounters a breakpoint, it pauses there,and the DevTools debugger shows the paused execution locationin the source area. In addition, the Call stack and Variablesareas populate with the current call stack for the paused isolate,and the local variables for the selected frame. Selecting otherframes in the Call stack area changes the contents of the variables.Within the Variables area, you can inspect individual objects bytoggling them open to see their fields. Hovering over an objectin the Variables area calls toString() for that object anddisplays the result.<topic_end><topic_start>Stepping through source codeWhen paused, the three stepping buttons become active.In addition, the Resume button continues regularexecution of the application.<topic_end><topic_start>Console outputConsole output for the running app (stdout and stderr) isdisplayed in the console, below the source code area.You can also see the output in the Logging view.<topic_end><topic_start>Breaking on exceptionsTo adjust the stop-on-exceptions behavior, toggle theIgnore dropdown at the top of the debugger view.Breaking on unhandled excepts only pauses execution if thebreakpoint is considered uncaught by the application code.Breaking on all exceptions causes the debugger to pausewhether or not the breakpoint was caught by application code.<topic_end><topic_start>Known issuesWhen performing a hot restart for a Flutter application,user breakpoints are cleared.<topic_end><topic_start>Other resourcesFor more information on debugging and profiling, see theDebugging page.<topic_end><topic_start>Using the Logging viewinfo Note  The logging view works with all Flutter and Dart applications.<topic_end><topic_start>What is it?The logging view displays events from the Dart runtime,application frameworks (like Flutter), and application-levellogging events.<topic_end><topic_start>Standard logging eventsBy default, the logging view shows:<topic_end><topic_start>Logging from your applicationTo implement logging in your code,see the Logging section in theDebugging Flutter apps programmaticallypage.<topic_end><topic_start>Clearing logsTo clear the log entries in the logging view,click the Clear logs button.<topic_end><topic_start>Other resourcesTo learn about different methods of loggingand how to effectively use DevTools toanalyze and debug Flutter apps faster,check out a guided Logging View tutorial.<topic_end><topic_start>Using the app size tool<topic_end><topic_start>What is it?The app size tool allows you to analyze the total size of your app.You can view a single snapshot of “size information”using the Analysis tab, or compare two differentsnapshots of “size information” using the Diff tab.<topic_end><topic_start>What is “size information”?“Size information” contains size data for Dart code,native code, and non-code elements of your app,like the application package, assets and fonts. A “sizeinformation” file contains data for the total pictureof your application size.<topic_end><topic_start>Dart size informationThe Dart AOT compiler performs tree-shaking on your codewhen compiling your application (profile or release modeonly—the AOT compiler is not used for debug builds,which are JIT compiled). This means that the compilerattempts to optimize your app’s size by removingpieces of code that are unused or unreachable.After the compiler optimizes your code as much as it can,the end result can be summarized as the collection of packages,libraries, classes, and functions that exist in the binary output,along with their size in bytes. This is the Dart portion of“size information” we can analyze in the app size tool to furtheroptimize Dart code and track down size issues.<topic_end><topic_start>How to use itIf DevTools is already connected to a running application,navigate to the “App Size” tab.If DevTools is not connected to a running application, you canaccess the tool from the landing page that appears once you have launchedDevTools (see installation instructions).<topic_end><topic_start>Analysis tabThe analysis tab allows you to inspect a single snapshotof size information.  You can view the hierarchical structureof the size data using the treemap and table,and you can view code attribution data(for example, why a piece of code is included in your compiledapplication) using the dominator tree and call graph.<topic_end><topic_start>Loading a size fileWhen you open the Analysis tab, you’ll see instructionsto load an app size file. Drag and drop an app sizefile into the dialog, and click “Analyze Size”.See Generating size files below for information ongenerating size files.<topic_end><topic_start>Treemap and tableThe treemap and table show the hierarchical data for your app’s size.<topic_end><topic_start>Using the treemapA treemap is a visualization for hierarchical data.The space is broken up into rectangles,where each rectangle is sized and ordered by some quantitativevariable (in this case, size in bytes).The area of each rectangle is proportional to the sizethe node occupies in the compiled application. Insideof each rectangle (call one A), there are additionalrectangles that exist one level deeper in the datahierarchy (children of A).To drill into a cell in the treemap, select the cell.This re-roots the tree so that the selected cell becomesthe visual root of the treemap.To navigate back, or up a level, use the breadcrumbnavigator at the top of the treemap.<topic_end><topic_start>Dominator tree and call graphThis section of the page shows code size attribution data(for example, why a piece of code is included in yourcompiled application). This data is visiblein the form of a dominator tree as well as a call graph.<topic_end><topic_start>Using the dominator treeA dominator tree is a tree where each node’schildren are those nodes it immediately dominates.A node a is said to “dominate” a node b ifevery path to b must go through a.To put it in context of app size analysis,imagine package:a imports both package:b and package:c,and both package:b and package:c import package:d.In this example, package:a dominates package:d,so the dominator tree for this data would look like:This information is helpful for understanding why certainpieces of code are present in your compiled application.For example, if you are analyzing your app size and findan unexpected package included in your compiled app, you canuse the dominator tree to trace the package to its root source.<topic_end><topic_start>Using the call graphA call graph provides similar information to the dominatortree in regards to helping you understand why code existsin a compiled application. However, instead of showingthe one-to-many dominant relationships between nodes of codesize data like the dominator tree, the call graph shows the many-to-manyrelationships that existing between nodes of code size data.Again, using the following example:The call graph for this data would link package:dto its direct callers, package:b and package:c,instead of its “dominator”, package:a.This information is useful for understanding thefine-grained dependencies of between pieces of your code(packages, libraries, classes, functions).<topic_end><topic_start>Should I use the dominator tree or the call graph?Use the dominator tree if you want to understand theroot cause for why a piece of code is included in yourapplication. Use the call graph if you want to understandall the call paths to and from a piece of code.A dominator tree is an analysis or slice of call graph data,where nodes are connected by “dominance” instead ofparent-child hierarchy. In the case where a parent nodedominates a child, the relationship in the call graph and thedominator tree would be identical, but this is not always the case.In the scenario where the call graph is complete(an edge exists between every pair of nodes),the dominator tree would show the that root is thedominator for every node in the graph.This is an example where the call graph would giveyou a better understanding around why a piece of code isincluded in your application.<topic_end><topic_start>Diff tabThe diff tab allows you to compare two snapshots ofsize information. The two size information filesyou are comparing should be generated from two differentversions of the same app; for example,the size file generated before and afterchanges to your code. You can visualize thedifference between the two data setsusing the treemap and table.<topic_end><topic_start>Loading size filesWhen you open the Diff tab,you’ll see instructions to load “old” and “new” sizefiles. Again, these files need to be generated fromthe same application. Drag and drop these files intotheir respective dialogs, and click Analyze Diff.See Generating size files below for informationon generating these files.<topic_end><topic_start>Treemap and tableIn the diff view, the treemap and tree table showonly data that differs between the two imported size files.For questions about using the treemap, see Using the treemap above.<topic_end><topic_start>Generating size filesTo use the app size tool, you’ll need to generate aFlutter size analysis file. This file contains sizeinformation for your entire application (native code,Dart code, assets, fonts, etc.), and you can generate it using the--analyze-size flag:This builds your application, prints a size summaryto the command line, and prints a linetelling you where to find the size analysis file.In this example, import the build/apk-code-size-analysis_01.jsonfile into the app size tool to analyze further.For more information, see App Size Documentation.<topic_end><topic_start>Other resourcesTo learn how to perform a step-by-step size analysis ofthe Wonderous App using DevTools, check out theApp Size Tool tutorial. Various strategiesto reduce an app’s size are also discussed.<topic_end><topic_start>DevTools extensions<topic_end><topic_start>What are DevTools extensions?DevTools extensions are developertools provided by third-party packages that are tightly integrated into theDevTools tooling suite. Extensions are distributed as part of a pub package,and they are dynamically loaded into DevTools when a user is debugging their app.<topic_end><topic_start>Use a DevTools extensionIf your app depends on a package that provides a DevTools extension, theextension automatically shows up in a new tab when you open DevTools.<topic_end><topic_start>Configure extension enablement statesYou need to manually enable the extension before it loads for the first time.Make sure the extension is provided by a source you trust before enabling it.Extension enablement states are stored in a devtools_options.yaml file in theroot of the user’s project (similar to analysis_options.yaml). This filestores per-project (or optionally, per user) settings for DevTools.If this file is checked into source control, the specified options areconfigured for the project. This means that anyone who pulls a project’ssource code and works on the project uses the same settings.If this file is omitted from source control, for example by addingdevtools_options.yaml as an entry in the .gitignore file, then the specifiedoptions are configured separately for each user. Since each user orcontributor to the project uses a local copy of the devtools_options.yamlfile in this case, the specified options might differ between project contributors.<topic_end><topic_start>Build a DevTools extensionFor an in-depth guide on how to build a DevTools extension, check out Dart and Flutter DevTools extensions, a free article on Medium.<topic_end><topic_start>DevTools release notesThis page summarizes the changes in official stable releases of DevTools.To view a complete list of changes, check out theDevTools git log.The Dart and Flutter SDKs include DevTools.To check your current version of DevTools,run the following on your command line:<topic_end><topic_start>Release notes<topic_end><topic_start>Flutter SDK overviewThe Flutter SDK has the packages and command-line tools that you need to developFlutter apps across platforms. To get the Flutter SDK, see Install.<topic_end><topic_start>What’s in the Flutter SDKThe following is available through the Flutter SDK:Note: For more information about the Flutter SDK, see itsREADME file.<topic_end><topic_start>flutter command-line toolThe flutter CLI tool (flutter/bin/flutter) is how developers(or IDEs on behalf of developers) interact with Flutter.<topic_end><topic_start>dart command-line toolThe dart CLI tool is available with the Flutter SDK at flutter/bin/dart.<topic_end><topic_start>Flutter and the pubspec fileinfo Note  This page is primarily aimed at folks who write  Flutter apps. If you write packages or plugins,   (perhaps you want to create a federated plugin),  you should check out the  Developing packages and plugins page.Every Flutter project includes a pubspec.yaml file,often referred to as the pubspec.A basic pubspec is generated when you createa new Flutter project. It’s located at the topof the project tree and contains metadata aboutthe project that the Dart and Flutter toolingneeds to know. The pubspec is written inYAML, which is human readable, but be awarethat white space (tabs v spaces) matters.The pubspec file specifies dependenciesthat the project requires, such as particular packages(and their versions), fonts, or image files.It also specifies other requirements, such as dependencies on developer packages (liketesting or mocking packages), or particularconstraints on the version of the Flutter SDK.Fields common to both Dart and Flutter projectsare described in the pubspec file on dart.dev.This page lists Flutter-specific fieldsthat are only valid for a Flutter project.info Note  The first time you build your project, it  creates a pubspec.lock file that contains  specific versions of the included packages.  This ensures that you get the same version  the next time the project is built.When you create a new project with theflutter create command (or by using theequivalent button in your IDE), it createsa pubspec for a basic Flutter app.Here is an example of a Flutter project pubspec file.The Flutter only fields are highlighted.<topic_end><topic_start>AssetsCommon types of assets include static data(for example, JSON files), configuration files,icons, and images (JPEG, WebP, GIF,animated WebP/GIF, PNG, BMP, and WBMP).Besides listing the images that are included in theapp package, an image asset can also refer to one or moreresolution-specific “variants”. For more information,see the resolution aware section of theAssets and images page.For information on adding assets from packagedependencies, see theasset images in package dependenciessection in the same page.<topic_end><topic_start>FontsAs shown in the above example,each entry in the fonts section should have afamily key with the font family name,and a fonts key with a list specifying theasset and other descriptors for the font.For examples of using fontssee the Use a custom font andExport fonts from a package recipes in theFlutter cookbook.<topic_end><topic_start>More informationFor more information on packages, plugins,and pubspec files, see the following:<topic_end><topic_start>Flutter fixAs Flutter continues to evolve, we provide a tool to help you clean updeprecated APIs from your codebase. The tool ships as part of Flutter, andsuggests changes that you might want to make to your code. The tool is availablefrom the command line, and is also integrated into the IDE plugins for AndroidStudio and Visual Studio Code.lightbulb Tip  These automated updates are called quick-fixes in IntelliJ and Android  Studio, and code actions in VS Code.<topic_end><topic_start>Applying individual fixesYou can use any supported IDEto apply a single fix at a time.<topic_end><topic_start>IntelliJ and Android StudioWhen the analyzer detects a deprecated API,a light bulb appears on that line of code.Clicking the light bulb displays the suggested fixthat updates that code to the new API.Clicking the suggested fix performs the update.A sample quick-fix in IntelliJ<topic_end><topic_start>VS CodeWhen the analyzer detects a deprecated API,it presents an error.You can do any of the following:Hover over the error and then click theQuick Fix link.This presents a filtered list showingonly fixes.Put the caret in the code with the error and clickthe light bulb icon that appears.This shows a list of all actions, includingrefactors.Put the caret in the code with the error andpress the shortcut(Command+. on macOS, Control+. elsewhere)This shows a list of all actions, includingrefactors.A sample code action in VS Code<topic_end><topic_start>Applying project-wide fixesdart fix Decoding FlutterTo see or apply changes to an entire project,you can use the command-line tool, dart fix.This tool has two options:To see a full list of available changes, runthe following command:To apply all changes in bulk, run thefollowing command:For more information on Flutter deprecations, seeDeprecation lifetime in Flutter, a free articleon Flutter’s Medium publication.<topic_end><topic_start>Code formattingWhile your code might follow any preferred style—in ourexperience—teams of developers might find it more productive to:The alternative is often tiring formatting debates during code reviews,where time might be better spent on code behavior rather than code style.<topic_end><topic_start>Automatically formatting code in VS CodeInstall the Flutter extension (seeEditor setup)to get automatic formatting of code in VS Code.To automatically format the code in the current source code window,right-click in the code window and select Format Document.You can add a keyboard shortcut to this VS Code Preferences.To automatically format code whenever you save a file, set theeditor.formatOnSave setting to true.<topic_end><topic_start>Automatically formatting code in Android Studio and IntelliJInstall the Dart plugin (seeEditor setup)to get automatic formatting of code in Android Studio and IntelliJ.To format your code in the current source code window:Android Studio and IntelliJ also provide a checkbox namedFormat code on save on the Flutter page in Preferenceson macOS or Settings on Windows and Linux.This option corrects formatting in the current file when you save it.<topic_end><topic_start>Automatically formatting code with the dart commandTo correct code formatting in the command line interface (CLI),run the dart format command:<topic_end><topic_start>Using trailing commasFlutter code often involves building fairly deep tree-shaped data structures,for example in a build method. To get good automatic formatting,we recommend you adopt the optional trailing commas.The guideline for adding a trailing comma is simple: Alwaysadd a trailing comma at the end of a parameter list infunctions, methods, and constructors where you care aboutkeeping the formatting you crafted.This helps the automatic formatter to insert an appropriateamount of line breaks for Flutter-style code.Here is an example of automatically formatted code with trailing commas:And the same code automatically formatted code without trailing commas:<topic_end><topic_start>Flutter architectural overviewThis article is intended to provide a high-level overview of the architecture ofFlutter, including the core principles and concepts that form its design.Flutter is a cross-platform UI toolkit that is designed to allow code reuseacross operating systems such as iOS and Android, while also allowingapplications to interface directly with underlying platform services. The goalis to enable developers to deliver high-performance apps that feel natural ondifferent platforms, embracing differences where they exist while sharing asmuch code as possible.During development, Flutter apps run in a VM that offers stateful hot reload ofchanges without needing a full recompile. For release, Flutter apps are compileddirectly to machine code, whether Intel x64 or ARM instructions, or toJavaScript if targeting the web. The framework is open source, with a permissiveBSD license, and has a thriving ecosystem of third-party packages thatsupplement the core library functionality.This overview is divided into a number of sections:<topic_end><topic_start>Architectural layersFlutter is designed as an extensible, layered system. It exists as a series ofindependent libraries that each depend on the underlying layer. No layer hasprivileged access to the layer below, and every part of the framework level isdesigned to be optional and replaceable.To the underlying operating system, Flutter applications are packaged in thesame way as any other native application. A platform-specific embedder providesan entrypoint; coordinates with the underlying operating system for access toservices like rendering surfaces, accessibility, and input; and manages themessage event loop. The embedder is written in a language that is appropriatefor the platform: currently Java and C++ for Android, Objective-C/Objective-C++for iOS and macOS, and C++ for Windows and Linux. Using the embedder, Fluttercode can be integrated into an existing application as a module, or the code maybe the entire content of the application. Flutter includes a number of embeddersfor common target platforms, but other embedders alsoexist.At the core of Flutter is the Flutter engine,which is mostly written in C++ and supportsthe primitives necessary to support all Flutter applications.The engine is responsible for rasterizing composited sceneswhenever a new frame needs to be painted.It provides the low-level implementation of Flutter’s core API,including graphics (through Impeller on iOS and coming to Android,and Skia on other platforms) text layout,file and network I/O, accessibility support,plugin architecture, and a Dart runtimeand compile toolchain.The engine is exposed to the Flutter framework throughdart:ui,which wraps the underlying C++ code in Dart classes. This libraryexposes the lowest-level primitives, such as classes for driving input,graphics, and text rendering subsystems.Typically, developers interact with Flutter through the Flutter framework,which provides a modern, reactive framework written in the Dart language. Itincludes a rich set of platform, layout, and foundational libraries, composed ofa series of layers. Working from the bottom to the top, we have:The Flutter framework is relatively small; many higher-level features thatdevelopers might use are implemented as packages, including platform pluginslike camera andwebview, as well as platform-agnosticfeatures like characters,http, andanimations that build upon the core Dart andFlutter libraries. Some of these packages come from the broader ecosystem,covering services like in-apppayments, Appleauthentication, andanimations.The rest of this overview broadly navigates down the layers, starting with thereactive paradigm of UI development. Then, we describe how widgets are composedtogether and converted into objects that can be rendered as part of anapplication. We describe how Flutter interoperates with other code at a platformlevel, before giving a brief summary of how Flutter’s web support differs fromother targets.<topic_end><topic_start>Anatomy of an appThe following diagram gives an overview of the piecesthat make up a regular Flutter app generated by flutter create.It shows where the Flutter Engine sits in this stack,highlights API boundaries, and identifies the repositorieswhere the individual pieces live. The legend below clarifiessome of the terminology commonly used to describe thepieces of a Flutter app.Dart AppFramework (source code)Engine (source code)Embedder (source code)Runner<topic_end><topic_start>Reactive user interfacesOn the surface, Flutter is a reactive, declarative UI framework,in which the developer provides a mapping from application state to interfacestate, and the framework takes on the task of updating the interface at runtimewhen the application state changes. This model is inspired bywork that came from Facebook for their own React framework,which includes a rethinking of many traditional design principles.In most traditional UI frameworks, the user interface’s initial state isdescribed once and then separately updated by user code at runtime, in responseto events. One challenge of this approach is that, as the application grows incomplexity, the developer needs to be aware of how state changes cascadethroughout the entire UI. For example, consider the following UI:There are many places where the state can be changed: the color box, the hueslider, the radio buttons. As the user interacts with the UI, changes must bereflected in every other place. Worse, unless care is taken, a minor change toone part of the user interface can cause ripple effects to seemingly unrelatedpieces of code.One solution to this is an approach like MVC, where you push data changes to themodel via the controller, and then the model pushes the new state to the viewvia the controller. However, this also is problematic, since creating andupdating UI elements are two separate steps that can easily get out of sync.Flutter, along with other reactive frameworks, takes an alternative approach tothis problem, by explicitly decoupling the user interface from its underlyingstate. With React-style APIs, you only create the UI description, and theframework takes care of using that one configuration to both create and/orupdate the user interface as appropriate.In Flutter, widgets (akin to components in React) are represented by immutableclasses that are used to configure a tree of objects. These widgets are used tomanage a separate tree of objects for layout, which is then used to manage aseparate tree of objects for compositing. Flutter is, at its core, a series ofmechanisms for efficiently walking the modified parts of trees, converting treesof objects into lower-level trees of objects, and propagating changes acrossthese trees.A widget declares its user interface by overriding the build() method, whichis a function that converts state to UI:The build() method is by design fast to execute and should be free of sideeffects, allowing it to be called by the framework whenever needed (potentiallyas often as once per rendered frame).This approach relies on certain characteristics of a language runtime (inparticular, fast object instantiation and deletion). Fortunately, Dart isparticularly well suited for thistask.<topic_end><topic_start>WidgetsAs mentioned, Flutter emphasizes widgets as a unit of composition. Widgets arethe building blocks of a Flutter app’s user interface, and each widget is animmutable declaration of part of the user interface.Widgets form a hierarchy based on composition. Each widget nests inside itsparent and can receive context from the parent. This structure carries all theway up to the root widget (the container that hosts the Flutter app, typicallyMaterialApp or CupertinoApp), as this trivial example shows:<code_start>import 'package:flutter/material.dart';import 'package:flutter/services.dart';void main() => runApp(const MyApp());class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return MaterialApp(      home: Scaffold(        appBar: AppBar(          title: const Text('My Home Page'),        ),        body: Center(          child: Builder(            builder: (context) {              return Column(                children: [                  const Text('Hello World'),                  const SizedBox(height: 20),                  ElevatedButton(                    onPressed: () {                      print('Click!');                    },                    child: const Text('A button'),                  ),                ],              );            },          ),        ),      ),    );  }}<code_end>In the preceding code, all instantiated classes are widgets.Apps update their user interface in response to events (such as a userinteraction) by telling the framework to replace a widget in the hierarchy withanother widget. The framework then compares the new and old widgets, andefficiently updates the user interface.Flutter has its own implementations of each UI control, rather than deferring tothose provided by the system: for example, there is a pure Dartimplementation of both theiOS Togglecontroland the one for theAndroid equivalent.This approach provides several benefits:<topic_end><topic_start>CompositionWidgets are typically composed of many other small, single-purpose widgets thatcombine to produce powerful effects.Where possible, the number of design concepts is kept to a minimum whileallowing the total vocabulary to be large. For example, in the widgets layer,Flutter uses the same core concept (a Widget) to represent drawing to thescreen, layout (positioning and sizing), user interactivity, state management,theming, animations, and navigation. In the animation layer, a pair of concepts,Animations and Tweens, cover most of the design space. In the renderinglayer, RenderObjects are used to describe layout, painting, hit testing, andaccessibility. In each of these cases, the corresponding vocabulary ends upbeing large: there are hundreds of widgets and render objects, and dozens ofanimation and tween types.The class hierarchy is deliberately shallow and broad to maximize the possiblenumber of combinations, focusing on small, composable widgets that each do onething well. Core features are abstract, with even basic features like paddingand alignment being implemented as separate components rather than being builtinto the core. (This also contrasts with more traditional APIs where featureslike padding are built in to the common core of every layout component.) So, forexample, to center a widget, rather than adjusting a notional Align property,you wrap it in a Centerwidget.There are widgets for padding, alignment, rows, columns, and grids. These layoutwidgets do not have a visual representation of their own. Instead, their solepurpose is to control some aspect of another widget’s layout. Flutter alsoincludes utility widgets that take advantage of this compositional approach.For example, Container, acommonly used widget, is made up of several widgets responsible for layout,painting, positioning, and sizing. Specifically, Container is made up of theLimitedBox,ConstrainedBox,Align,Padding,DecoratedBox, andTransform widgets, as youcan see by reading its source code. A defining characteristic of Flutter is thatyou can drill down into the source for any widget and examine it. So, ratherthan subclassing Container to produce a customized effect, you can compose itand other widgets in novel ways, or just create a new widget usingContainer as inspiration.<topic_end><topic_start>Building widgetsAs mentioned earlier, you determine the visual representation of a widget byoverriding thebuild() function toreturn a new element tree. This tree represents the widget’s part of the userinterface in more concrete terms. For example, a toolbar widget might have abuild function that returns a horizontallayout of sometext andvariousbuttons. As needed,the framework recursively asks each widget to build until the tree is entirelydescribed by concrete renderableobjects. Theframework then stitches together the renderable objects into a renderable objecttree.A widget’s build function should be free of side effects. Whenever the functionis asked to build, the widget should return a new tree of widgets1, regardless of what the widget previously returned. Theframework does the heavy lifting work to determine which build methods need tobe called based on the render object tree (described in more detail later). Moreinformation about this process can be found in the Inside Fluttertopic.On each rendered frame, Flutter can recreate just the parts of the UI where thestate has changed by calling that widget’s build() method. Therefore it isimportant that build methods should return quickly, and heavy computational workshould be done in some asynchronous manner and then stored as part of the stateto be used by a build method.While relatively naïve in approach, this automated comparison is quiteeffective, enabling high-performance, interactive apps. And, the design of thebuild function simplifies your code by focusing on declaring what a widget ismade of, rather than the complexities of updating the user interface from onestate to another.<topic_end><topic_start>Widget stateThe framework introduces two major classes of widget: stateful and statelesswidgets.Many widgets have no mutable state: they don’t have any properties that changeover time (for example, an icon or a label). These widgets subclassStatelessWidget.However, if the unique characteristics of a widget need to change based on userinteraction or other factors, that widget is stateful. For example, if awidget has a counter that increments whenever the user taps a button, then thevalue of the counter is the state for that widget. When that value changes, thewidget needs to be rebuilt to update its part of the UI. These widgets subclassStatefulWidget, and(because the widget itself is immutable) they store mutable state in a separateclass that subclasses State.StatefulWidgets don’t have a build method; instead, their user interface isbuilt through their State object.Whenever you mutate a State object (for example, by incrementing the counter),you must call setState()to signal the framework to update the user interface by calling the State’sbuild method again.Having separate state and widget objects lets other widgets treat both statelessand stateful widgets in exactly the same way, without being concerned aboutlosing state. Instead of needing to hold on to a child to preserve its state,the parent can create a new instance of the child at any time without losing thechild’s persistent state. The framework does all the work of finding and reusingexisting state objects when appropriate.<topic_end><topic_start>State managementSo, if many widgets can contain state, how is state managed and passed aroundthe system?As with any other class, you can use a constructor in a widget to initialize itsdata, so a build() method can ensure that any child widget is instantiatedwith the data it needs:As widget trees get deeper, however, passing state information up and down thetree hierarchy becomes cumbersome. So, a third widget type,InheritedWidget,provides an easy way to grab data from a shared ancestor. You can useInheritedWidget to create a state widget that wraps a common ancestor in thewidget tree, as shown in this example:Whenever one of the ExamWidget or GradeWidget objects needs data fromStudentState, it can now access it with a command such as:The of(context) call takes the build context (a handle to the current widgetlocation), and returns the nearest ancestor in thetreethat matches the StudentState type. InheritedWidgets also offer anupdateShouldNotify() method, which Flutter calls to determine whether a statechange should trigger a rebuild of child widgets that use it.Flutter itself uses InheritedWidget extensively as part of the framework forshared state, such as the application’s visual theme, which includesproperties like color and typestyles that arepervasive throughout an application. The MaterialApp build() method insertsa theme in the tree when it builds, and then deeper in the hierarchy a widgetcan use the .of() method to look up the relevant theme data, for example:<code_start>Container(  color: Theme.of(context).secondaryHeaderColor,  child: Text(    'Text with a background color',    style: Theme.of(context).textTheme.titleLarge,  ),);<code_end>As applications grow, more advanced state management approaches that reduce theceremony of creating and using stateful widgets become more attractive. ManyFlutter apps use utility packages likeprovider, which provides a wrapper aroundInheritedWidget. Flutter’s layered architecture also enables alternativeapproaches to implement the transformation of state into UI, such as theflutter_hooks package.<topic_end><topic_start>Rendering and layoutThis section describes the rendering pipeline, which is the series of steps thatFlutter takes to convert a hierarchy of widgets into the actual pixels paintedonto a screen.<topic_end><topic_start>Flutter’s rendering modelYou may be wondering: if Flutter is a cross-platform framework, then how can itoffer comparable performance to single-platform frameworks?It’s useful to start by thinking about how traditionalAndroid apps work. When drawing,you first call the Java code of the Android framework.The Android system libraries provide the componentsresponsible for drawing themselves to a Canvas object,which Android can then render with Skia,a graphics engine written in C/C++ that calls theCPU or GPU to complete the drawing on the device.Cross-platform frameworks typically work by creatingan abstraction layer over the underlying nativeAndroid and iOS UI libraries, attempting to smooth out theinconsistencies of each platform representation.App code is often written in an interpreted language like JavaScript,which must in turn interact with the Java-basedAndroid or Objective-C-based iOS system libraries to display UI.All this adds overhead that can be significant,particularly where there is a lot ofinteraction between the UI and the app logic.By contrast, Flutter minimizes those abstractions,bypassing the system UI widget libraries in favorof its own widget set. The Dart code that paintsFlutter’s visuals is compiled into native code,which uses Skia (or, in the future, Impeller) for rendering.Flutter also embeds its own copy of Skia as part of the engine,allowing the developer to upgrade their app to stayupdated with the latest performance improvementseven if the phone hasn’t been updated with a new Android version.The same is true for Flutter on other native platforms,such as Windows or macOS.info Note  Flutter 3.10 set Impeller as the default  rendering engine on iOS. It’s in preview  for Android behind a flag.<topic_end><topic_start>From user input to the GPUThe overriding principle that Flutter applies to its rendering pipeline is thatsimple is fast. Flutter has a straightforward pipeline for how data flows tothe system, as shown in the following sequencing diagram:Let’s take a look at some of these phases in greater detail.<topic_end><topic_start>Build: from Widget to ElementConsider this code fragment that demonstrates a widget hierarchy:<code_start>Container(  color: Colors.blue,  child: Row(    children: [      Image.network('https://www.example.com/1.png'),      const Text('A'),    ],  ),);<code_end>When Flutter needs to render this fragment, it calls the build() method, whichreturns a subtree of widgets that renders UI based on the current app state.During this process, the build() method can introduce new widgets, asnecessary, based on its state. As an example, in the preceding codefragment, Container has color and child properties. From looking at thesourcecodefor Container, you can see that if the color is not null, it inserts aColoredBox representing the color:Correspondingly, the Image and Text widgets might insert child widgets suchas RawImage and RichText during the build process. The eventual widgethierarchy may therefore be deeper than what the code represents, as in thiscase2:This explains why, when you examine the tree through a debug tool such as theFlutter inspector, part of theDart DevTools, you might see a structure that is considerably deeper than whatis in your original code.During the build phase, Flutter translates the widgets expressed in code into acorresponding element tree, with one element for every widget. Each elementrepresents a specific instance of a widget in a given location of the treehierarchy. There are two basic types of elements:RenderObjectElements are an intermediary between their widget analog and theunderlying RenderObject, which we’ll come to later.The element for any widget can be referenced through its BuildContext, whichis a handle to the location of a widget in the tree. This is the context in afunction call such as Theme.of(context), and is supplied to the build()method as a parameter.Because widgets are immutable, including the parent/child relationship betweennodes, any change to the widget tree (such as changing Text('A') toText('B') in the preceding example) causes a new set of widget objects to bereturned. But that doesn’t mean the underlying representation must be rebuilt.The element tree is persistent from frame to frame, and therefore plays acritical performance role, allowing Flutter to act as if the widget hierarchy isfully disposable while caching its underlying representation. By only walkingthrough the widgets that changed, Flutter can rebuild just the parts of theelement tree that require reconfiguration.<topic_end><topic_start>Layout and renderingIt would be a rare application that drew only a single widget. An important partof any UI framework is therefore the ability to efficiently lay out a hierarchyof widgets, determining the size and position of each element before they arerendered on the screen.The base class for every node in the render tree isRenderObject, whichdefines an abstract model for layout and painting. This is extremely general: itdoes not commit to a fixed number of dimensions or even a Cartesian coordinatesystem (demonstrated by this example of a polar coordinatesystem). EachRenderObject knows its parent, but knows little about its children other thanhow to visit them and their constraints. This provides RenderObject withsufficient abstraction to be able to handle a variety of use cases.During the build phase, Flutter creates or updates an object that inherits fromRenderObject for each RenderObjectElement in the element tree.RenderObjects are primitives:RenderParagraphrenders text,RenderImage rendersan image, andRenderTransformapplies a transformation before painting its child.Most Flutter widgets are rendered by an object that inherits from theRenderBox subclass, which represents a RenderObject of fixed size in a 2DCartesian space. RenderBox provides the basis of a box constraint model,establishing a minimum and maximum width and height for each widget to berendered.To perform layout, Flutter walks the render tree in a depth-first traversal andpasses down size constraints from parent to child. In determining its size,the child must respect the constraints given to it by its parent. Childrenrespond by passing up a size to their parent object within the constraintsthe parent established.At the end of this single walk through the tree, every object has a defined sizewithin its parent’s constraints and is ready to be painted by calling thepaint()method.The box constraint model is very powerful as a way to layout objects in O(n)time:This model works even when a child object needs to know how much space it hasavailable to decide how it will render its content. By using aLayoutBuilder widget,the child object can examine the passed-down constraints and use those todetermine how it will use them, for example:<code_start>Widget build(BuildContext context) {  return LayoutBuilder(    builder: (context, constraints) {      if (constraints.maxWidth < 600) {        return const OneColumnLayout();      } else {        return const TwoColumnLayout();      }    },  );}<code_end>More information about the constraint and layout system,along with working examples, can be found in theUnderstanding constraints topic.The root of all RenderObjects is the RenderView, which represents the totaloutput of the render tree. When the platform demands a new frame to be rendered(for example, because of avsync or becausea texture decompression/upload is complete), a call is made to thecompositeFrame() method, which is part of the RenderView object at the rootof the render tree. This creates a SceneBuilder to trigger an update of thescene. When the scene is complete, the RenderView object passes the compositedscene to the Window.render() method in dart:ui, which passes control to theGPU to render it.Further details of the composition and rasterization stages of the pipeline arebeyond the scope of this high-level article, but more information can be foundin this talk on the Flutter renderingpipeline.<topic_end><topic_start>Platform embeddingAs we’ve seen, rather than being translated into the equivalent OS widgets,Flutter user interfaces are built, laid out, composited, and painted by Flutteritself. The mechanism for obtaining the texture and participating in the applifecycle of the underlying operating system inevitably varies depending on theunique concerns of that platform. The engine is platform-agnostic, presenting astable ABI (Application BinaryInterface)that provides a platform embedder with a way to set up and use Flutter.The platform embedder is the native OS application that hosts all Fluttercontent, and acts as the glue between the host operating system and Flutter.When you start a Flutter app, the embedder provides the entrypoint, initializesthe Flutter engine, obtains threads for UI and rastering, and creates a texturethat Flutter can write to. The embedder is also responsible for the applifecycle, including input gestures (such as mouse, keyboard, touch), windowsizing, thread management, and platform messages. Flutter includes platformembedders for Android, iOS, Windows, macOS, and Linux; you can also create acustom platform embedder, as in this workedexample that supports remotingFlutter sessions through a VNC-style framebuffer or this worked example forRaspberry Pi.Each platform has its own set of APIs and constraints. Some briefplatform-specific notes:<topic_end><topic_start>Integrating with other codeFlutter provides a variety of interoperability mechanisms, whether you’reaccessing code or APIs written in a language like Kotlin or Swift, calling anative C-based API, embedding native controls in a Flutter app, or embeddingFlutter in an existing application.<topic_end><topic_start>Platform channelsFor mobile and desktop apps, Flutter allows you to call into custom code througha platform channel, which is a mechanism for communicating between yourDart code and the platform-specific code of your host app. By creating a commonchannel (encapsulating a name and a codec), you can send and receive messagesbetween Dart and a platform component written in a language like Kotlin orSwift. Data is serialized from a Dart type like Map into a standard format,and then deserialized into an equivalent representation in Kotlin (such asHashMap) or Swift (such as Dictionary).The following is a short platform channel example of a Dart call to a receivingevent handler in Kotlin (Android) or Swift (iOS):<code_start>// Dart sideconst channel = MethodChannel('foo');final greeting = await channel.invokeMethod('bar', 'world') as String;print(greeting);<code_end>Further examples of using platform channels, including examples for desktopplatforms, can be found in the flutter/packagesrepository. There are also thousands of pluginsalready available for Flutter that cover many commonscenarios, ranging from Firebase to ads to device hardware like camera andBluetooth.<topic_end><topic_start>Foreign Function InterfaceFor C-based APIs, including those that can be generated for code written inmodern languages like Rust or Go, Dart provides a direct mechanism for bindingto native code using the dart:ffi library. The foreign function interface(FFI) model can be considerably faster than platform channels, because noserialization is required to pass data. Instead, the Dart runtime provides theability to allocate memory on the heap that is backed by a Dart object and makecalls to statically or dynamically linked libraries. FFI is available for allplatforms other than web, where the js packageserves an equivalent purpose.To use FFI, you create a typedef for each of the Dart and unmanaged methodsignatures, and instruct the Dart VM to map between them. As an example,here’s a fragment of code to call the traditional Win32 MessageBox() API:<code_start>import 'dart:ffi';import 'package:ffi/ffi.dart'; // contains .toNativeUtf16() extension methodtypedef MessageBoxNative = Int32 Function(  IntPtr hWnd,  Pointer<Utf16> lpText,  Pointer<Utf16> lpCaption,  Int32 uType,);typedef MessageBoxDart = int Function(  int hWnd,  Pointer<Utf16> lpText,  Pointer<Utf16> lpCaption,  int uType,);void exampleFfi() {  final user32 = DynamicLibrary.open('user32.dll');  final messageBox =      user32.lookupFunction<MessageBoxNative, MessageBoxDart>('MessageBoxW');  final result = messageBox(    0, // No owner window    'Test message'.toNativeUtf16(), // Message    'Window caption'.toNativeUtf16(), // Window title    0, // OK button only  );}<code_end><topic_end><topic_start>Rendering native controls in a Flutter appBecause Flutter content is drawn to a texture and its widget tree is entirelyinternal, there’s no place for something like an Android view to exist withinFlutter’s internal model or render interleaved within Flutter widgets. That’s aproblem for developers that would like to include existing platform componentsin their Flutter apps, such as a browser control.Flutter solves this by introducing platform view widgets(AndroidViewand UiKitView)that let you embed this kind of content on each platform. Platform views can beintegrated with other Flutter content3. Each ofthese widgets acts as an intermediary to the underlying operating system. Forexample, on Android, AndroidView serves three primary functions:Inevitably, there is a certain amount of overhead associated with thissynchronization. In general, therefore, this approach is best suited for complexcontrols like Google Maps where reimplementing in Flutter isn’t practical.Typically, a Flutter app instantiates these widgets in a build() method basedon a platform test. As an example, from thegoogle_maps_flutter plugin:Communicating with the native code underlying the AndroidView or UiKitViewtypically occurs using the platform channels mechanism, as previously described.At present, platform views aren’t available for desktop platforms, but this isnot an architectural limitation; support might be added in the future.<topic_end><topic_start>Hosting Flutter content in a parent appThe converse of the preceding scenario is embedding a Flutter widget in anexisting Android or iOS app. As described in an earlier section, a newly createdFlutter app running on a mobile device is hosted in an Android activity or iOSUIViewController. Flutter content can be embedded into an existing Android oriOS app using the same embedding API.The Flutter module template is designed for easy embedding; you can either embedit as a source dependency into an existing Gradle or Xcode build definition, oryou can compile it into an Android Archive or iOS Framework binary for usewithout requiring every developer to have Flutter installed.The Flutter engine takes a short while to initialize, because it needs to loadFlutter shared libraries, initialize the Dart runtime, create and run a Dartisolate, and attach a rendering surface to the UI. To minimize any UI delayswhen presenting Flutter content, it’s best to initialize the Flutter engineduring the overall app initialization sequence, or at least ahead of the firstFlutter screen, so that users don’t experience a sudden pause while the firstFlutter code is loaded. In addition, separating the Flutter engine allows it tobe reused across multiple Flutter screens and share the memory overhead involvedwith loading the necessary libraries.More information about how Flutter is loaded into an existing Android or iOS appcan be found at the Load sequence, performance and memorytopic.<topic_end><topic_start>Flutter web supportWhile the general architectural concepts apply to all platforms that Fluttersupports, there are some unique characteristics of Flutter’s web support thatare worthy of comment.Dart has been compiling to JavaScript for as long as the language has existed,with a toolchain optimized for both development and production purposes. Manyimportant apps compile from Dart to JavaScript and run in production today,including the advertiser tooling for Google Ads.Because the Flutter framework is written in Dart, compiling it to JavaScript wasrelatively straightforward.However, the Flutter engine, written in C++,is designed to interface with theunderlying operating system rather than a web browser.A different approach is therefore required.On the web, Flutter provides a reimplementation of theengine on top of standard browser APIs.We currently have two options forrendering Flutter content on the web: HTML and WebGL.In HTML mode, Flutter uses HTML, CSS, Canvas, and SVG.To render to WebGL, Flutter uses a version of Skiacompiled to WebAssembly calledCanvasKit.While HTML mode offers the best code size characteristics,CanvasKit provides the fastest path to thebrowser’s graphics stack,and offers somewhat higher graphical fidelity with thenative mobile targets4.The web version of the architectural layer diagram is as follows:Perhaps the most notable difference compared to other platforms on which Flutterruns is that there is no need for Flutter to provide a Dart runtime. Instead,the Flutter framework (along with any code you write) is compiled to JavaScript.It’s also worthy to note that Dart has very few language semantic differencesacross all its modes (JIT versus AOT, native versus web compilation), and mostdevelopers will never write a line of code that runs into such a difference.During development time, Flutter web usesdartdevc, a compiler that supportsincremental compilation and therefore allows hot restart (although not currentlyhot reload) for apps. Conversely, when you are ready to create a production appfor the web, dart2js, Dart’shighly-optimized production JavaScript compiler is used, packaging the Fluttercore and framework along with your application into a minified source file thatcan be deployed to any web server. Code can be offered in a single file or splitinto multiple files through deferred imports.<topic_end><topic_start>Further informationFor those interested in more information about the internals of Flutter, theInside Flutter whitepaperprovides a useful guide to the framework’s design philosophy.Footnotes:1 While the build function returns a fresh tree,you only need to return something different if there’s some newconfiguration to incorporate. If the configuration is in fact the same, you canjust return the same widget.2 This is a slight simplification for ease ofreading. In practice, the tree might be more complex.3 There are some limitations with this approach, forexample, transparency doesn’t composite the same way for a platform view as itwould for other Flutter widgets.4 One example is shadows, which have to beapproximated with DOM-equivalent primitives at the cost of some fidelity.<topic_end><topic_start>Inside FlutterThis document describes the inner workings of the Flutter toolkit that makeFlutter’s API possible. Because Flutter widgets are built using aggressivecomposition, user interfaces built with Flutter have a large number ofwidgets.  To support this workload, Flutter uses sublinear algorithms forlayout and building widgets as well as data structures that make treesurgery efficient and that have a number of constant-factor optimizations.With some additional details, this design also makes it easy for developersto create infinite scrolling lists using callbacks that build exactly thosewidgets that are visible to the user.<topic_end><topic_start>Aggressive composabilityOne of the most distinctive aspects of Flutter is its aggressivecomposability. Widgets are built by composing other widgets,which are themselves built out of progressively more basic widgets.For example, Padding is a widget rather than a property of other widgets.As a result, user interfaces built with Flutter consist of many,many widgets.The widget building recursion bottoms out in RenderObjectWidgets,which are widgets that create nodes in the underlying render tree.The render tree is a data structure that stores the geometry of the userinterface, which is computed during layout and used during painting andhit testing. Most Flutter developers do not author render objects directlybut instead manipulate the render tree using widgets.In order to support aggressive composability at the widget layer,Flutter uses a number of efficient algorithms and optimizations atboth the widget and render tree layers, which are described in thefollowing subsections.<topic_end><topic_start>Sublinear layoutWith a large number of widgets and render objects, the key to goodperformance is efficient algorithms. Of paramount importance is theperformance of layout, which is the algorithm that determines thegeometry (for example, the size and position) of the render objects.Some other toolkits use layout algorithms that are O(N²) or worse(for example, fixed-point iteration in some constraint domain).Flutter aims for linear performance for initial layout, and sublinearlayout performance in the common case of subsequently updating anexisting layout. Typically, the amount of time spent in layout shouldscale more slowly than the number of render objects.Flutter performs one layout per frame, and the layout algorithm worksin a single pass. Constraints are passed down the tree by parentobjects calling the layout method on each of their children.The children recursively perform their own layout and then returngeometry up the tree by returning from their layout method. Importantly,once a render object has returned from its layout method, that renderobject will not be visited again1until the layout for the next frame. This approach combines what mightotherwise be separate measure and layout passes into a single pass and,as a result, each render object is visited at mosttwice2 during layout: once on the waydown the tree, and once on the way up the tree.Flutter has several specializations of this general protocol.The most common specialization is RenderBox, which operates intwo-dimensional, cartesian coordinates. In box layout, the constraintsare a min and max width and a min and max height. During layout,the child determines its geometry by choosing a size within these bounds.After the child returns from layout, the parent decides the child’sposition in the parent’s coordinate system3.Note that the child’s layout cannot depend on its position,as the position is not determined until after the childreturns from the layout. As a result, the parent is free to repositionthe child without needing to recompute its layout.More generally, during layout, the only information that flows fromparent to child are the constraints and the only information thatflows from child to parent is the geometry. These invariants can reducethe amount of work required during layout:If the child has not marked its own layout as dirty, the child canreturn immediately from layout, cutting off the walk, as long as theparent gives the child the same constraints as the child receivedduring the previous layout.Whenever a parent calls a child’s layout method, the parent indicateswhether it uses the size information returned from the child. If,as often happens, the parent does not use the size information,then the parent need not recompute its layout if the child selectsa new size because the parent is guaranteed that the new size willconform to the existing constraints.Tight constraints are those that can be satisfied by exactly onevalid geometry. For example, if the min and max widths are equal toeach other and the min and max heights are equal to each other,the only size that satisfies those constraints is one with thatwidth and height. If the parent provides tight constraints,then the parent need not recompute its layout whenever the childrecomputes its layout, even if the parent uses the child’s sizein its layout, because the child cannot change size without newconstraints from its parent.A render object can declare that it uses the constraints providedby the parent only to determine its geometry. Such a declarationinforms the framework that the parent of that render object doesnot need to recompute its layout when the child recomputes its layouteven if the constraints are not tight and even if the parent’slayout depends on the child’s size, because the child cannot changesize without new constraints from its parent.As a result of these optimizations, when the render object tree containsdirty nodes, only those nodes and a limited part of the subtree aroundthem are visited during layout.<topic_end><topic_start>Sublinear widget buildingSimilar to the layout algorithm, Flutter’s widget building algorithmis sublinear. After being built, the widgets are held by the elementtree, which retains the logical structure of the user interface.The element tree is necessary because the widgets themselves areimmutable, which means (among other things), they cannot remember theirparent or child relationships with other widgets. The element tree alsoholds the state objects associated with stateful widgets.In response to user input (or other stimuli), an element can become dirty,for example if the developer calls setState() on the associated stateobject. The framework keeps a list of dirty elements and jumps directlyto them during the build phase, skipping over clean elements. Duringthe build phase, information flows unidirectionally down the elementtree, which means each element is visited at most once during the buildphase.  Once cleaned, an element cannot become dirty again because,by induction, all its ancestor elements are alsoclean4.Because widgets are immutable, if an element has not marked itself asdirty, the element can return immediately from build, cutting off the walk,if the parent rebuilds the element with an identical widget. Moreover,the element need only compare the object identity of the two widgetreferences in order to establish that the new widget is the same asthe old widget. Developers exploit this optimization to implement thereprojection pattern, in which a widget includes a prebuilt childwidget stored as a member variable in its build.During build, Flutter also avoids walking the parent chain usingInheritedWidgets. If widgets commonly walked their parent chain,for example to determine the current theme color, the build phasewould become O(N²) in the depth of the tree, which can be quitelarge due to aggressive composition. To avoid these parent walks,the framework pushes information down the element tree by maintaininga hash table of InheritedWidgets at each element. Typically, manyelements will reference the same hash table, which changes only atelements that introduce a new InheritedWidget.<topic_end><topic_start>Linear reconciliationContrary to popular belief, Flutter does not employ a tree-diffingalgorithm. Instead, the framework decides whether to reuse elements byexamining the child list for each element independently using an O(N)algorithm. The child list reconciliation algorithm optimizes for thefollowing cases:The general approach is to match up the beginning and end of both childlists by comparing the runtime type and key of each widget,potentially finding a non-empty range in the middle of each listthat contains all the unmatched children. The framework then placesthe children in the range in the old child list into a hash tablebased on their keys. Next, the framework walks the range in the newchild list and queries the hash table by key for matches. Unmatchedchildren are discarded and rebuilt from scratch whereas matched childrenare rebuilt with their new widgets.<topic_end><topic_start>Tree surgeryReusing elements is important for performance because elements owntwo critical pieces of data: the state for stateful widgets and theunderlying render objects. When the framework is able to reuse an element,the state for that logical part of the user interface is preservedand the layout information computed previously can be reused,often avoiding entire subtree walks. In fact, reusing elements isso valuable that Flutter supports non-local tree mutations thatpreserve state and layout information.Developers can perform a non-local tree mutation by associating a GlobalKeywith one of their widgets. Each global key is unique throughout theentire application and is registered with a thread-specific hash table.During the build phase, the developer can move a widget with a globalkey to an arbitrary location in the element tree. Rather than buildinga fresh element at that location, the framework will check the hashtable and reparent the existing element from its previous location toits new location, preserving the entire subtree.The render objects in the reparented subtree are able to preservetheir layout information because the layout constraints are the onlyinformation that flows from parent to child in the render tree.The new parent is marked dirty for layout because its child list haschanged, but if the new parent passes the child the same layoutconstraints the child received from its old parent, the child canreturn immediately from layout, cutting off the walk.Global keys and non-local tree mutations are used extensively bydevelopers to achieve effects such as hero transitions and navigation.<topic_end><topic_start>Constant-factor optimizationsIn addition to these algorithmic optimizations, achieving aggressivecomposability also relies on several important constant-factoroptimizations. These optimizations are most important at the leaves ofthe major algorithms discussed above.Child-model agnostic. Unlike most toolkits, which use child lists,Flutter’s render tree does not commit to a specific child model.For example, the RenderBox class has an abstract visitChildren()method rather than a concrete firstChild and nextSibling interface.Many subclasses support only a single child, held directly as a membervariable, rather than a list of children. For example, RenderPaddingsupports only a single child and, as a result, has a simpler layoutmethod that takes less time to execute.Visual render tree, logical widget tree. In Flutter, the rendertree operates in a device-independent, visual coordinate system,which means smaller values in the x coordinate are always towardsthe left, even if the current reading direction is right-to-left.The widget tree typically operates in logical coordinates, meaningwith start and end values whose visual interpretation dependson the reading direction. The transformation from logical to visualcoordinates is done in the handoff between the widget tree and therender tree. This approach is more efficient because layout andpainting calculations in the render tree happen more often than thewidget-to-render tree handoff and can avoid repeated coordinate conversions.Text handled by a specialized render object. The vast majorityof render objects are ignorant of the complexities of text. Instead,text is handled by a specialized render object, RenderParagraph,which is a leaf in the render tree. Rather than subclassing atext-aware render object, developers incorporate text into theiruser interface using composition. This pattern means RenderParagraphcan avoid recomputing its text layout as long as its parent suppliesthe same layout constraints, which is common, even during tree surgery.Observable objects. Flutter uses both the model-observation andthe reactive paradigms. Obviously, the reactive paradigm is dominant,but Flutter uses observable model objects for some leaf data structures.For example, Animations notify an observer list when their value changes.Flutter hands off these observable objects from the widget tree to therender tree, which observes them directly and invalidates only theappropriate stage of the pipeline when they change. For example,a change to an Animation<Color> might trigger only the paint phaserather than both the build and paint phases.Taken together and summed over the large trees created by aggressivecomposition, these optimizations have a substantial effect on performance.<topic_end><topic_start>Separation of the Element and RenderObject treesThe RenderObject and Element (Widget) trees in Flutter are isomorphic(strictly speaking, the RenderObject tree is a subset of the Elementtree). An obvious simplification would be to combine these trees intoone tree. However, in practice there are a number of benefits to havingthese trees be separate:Performance. When the layout changes, only the relevant parts ofthe layout tree need to be walked. Due to composition, the elementtree frequently has many additional nodes that would have to be skipped.Clarity. The clearer separation of concerns allows the widgetprotocol and the render object protocol to each be specialized totheir specific needs, simplifying the API surface and thus loweringthe risk of bugs and the testing burden.Type safety. The render object tree can be more type safe since itcan guarantee at runtime that children will be of the appropriate type(each coordinate system, e.g. has its own type of render object).Composition widgets can be agnostic about the coordinate system usedduring layout (for example, the same widget exposing a part of the appmodel could be used in both a box layout and a sliver layout), and thusin the element tree, verifying the type of render objects would requirea tree walk.<topic_end><topic_start>Infinite scrollingInfinite scrolling lists are notoriously difficult for toolkits.Flutter supports infinite scrolling lists with a simple interfacebased on the builder pattern, in which a ListView uses a callbackto build widgets on demand as they become visible to the user duringscrolling. Supporting this feature requires viewport-aware layoutand building widgets on demand.<topic_end><topic_start>Viewport-aware layoutLike most things in Flutter, scrollable widgets are built usingcomposition. The outside of a scrollable widget is a Viewport,which is a box that is “bigger on the inside,” meaning its childrencan extend beyond the bounds of the viewport and can be scrolled intoview. However, rather than having RenderBox children, a viewport hasRenderSliver children, known as slivers, which have a viewport-awarelayout protocol.The sliver layout protocol matches the structure of the box layoutprotocol in that parents pass constraints down to their children andreceive geometry in return. However, the constraint and geometry datadiffers between the two protocols. In the sliver protocol, childrenare given information about the viewport, including the amount ofvisible space remaining. The geometry data they return enables avariety of scroll-linked effects, including collapsible headers andparallax.Different slivers fill the space available in the viewport in differentways. For example, a sliver that produces a linear list of children laysout  each child in order until the sliver either runs out of children orruns out of space. Similarly, a sliver that produces a two-dimensionalgrid of children fills only the portion of its grid that is visible.Because they are aware of how much space is visible, slivers can producea finite number of children even if they have the potential to producean unbounded number of children.Slivers can be composed to create bespoke scrollable layouts and effects.For example, a single viewport can have a collapsible header followedby a linear list and then a grid. All three slivers will cooperate throughthe sliver layout protocol to produce only those children that are actuallyvisible through the viewport, regardless of whether those children belongto the header, the list, or the grid6.<topic_end><topic_start>Building widgets on demandIf Flutter had a strict build-then-layout-then-paint pipeline,the foregoing would be insufficient to implement an infinite scrollinglist because the information about how much space is visible throughthe viewport is available only during the layout phase. Withoutadditional machinery, the layout phase is too late to build thewidgets necessary to fill the space. Flutter solves this problemby interleaving the build and layout phases of the pipeline. At anypoint in the layout phase, the framework can start building newwidgets on demand as long as those widgets are descendants of therender object currently performing layout.Interleaving build and layout is possible only because of the strictcontrols on information propagation in the build and layout algorithms.Specifically, during the build phase, information can propagate onlydown the tree. When a render object is performing layout, the layoutwalk has not visited the subtree below that render object, which meanswrites generated by building in that subtree cannot invalidate anyinformation that has entered the layout calculation thus far. Similarly,once layout has returned from a render object, that render object willnever be visited again during this layout, which means any writesgenerated by subsequent layout calculations cannot invalidate theinformation used to build the render object’s subtree.Additionally, linear reconciliation and tree surgery are essentialfor efficiently updating elements during scrolling and for modifyingthe render tree when elements are scrolled into and out of view atthe edge of the viewport.<topic_end><topic_start>API ErgonomicsBeing fast only matters if the framework can actually be used effectively.To guide Flutter’s API design towards greater usability, Flutter has beenrepeatedly tested in extensive UX studies with developers. These studiessometimes confirmed pre-existing design decisions, sometimes helped guidethe prioritization of features, and sometimes changed the direction of theAPI design. For instance, Flutter’s APIs are heavily documented; UXstudies confirmed the value of such documentation, but also highlightedthe need specifically for sample code and illustrative diagrams.This section discusses some of the decisions made in Flutter’s API designin aid of usability.<topic_end><topic_start>Specializing APIs to match the developer’s mindsetThe base class for nodes in Flutter’s Widget, Element, and RenderObjecttrees does not define a child model. This allows each node to bespecialized for the child model that is applicable to that node.Most Widget objects have a single child Widget, and therefore only exposea single child parameter. Some widgets support an arbitrary number ofchildren, and expose a children parameter that takes a list.Some widgets don’t have any children at all and reserve no memory,and have no parameters for them. Similarly, RenderObjects expose APIsspecific to their child model. RenderImage is a leaf node, and has noconcept of children. RenderPadding takes a single child, so it has storagefor a single pointer to a single child. RenderFlex takes an arbitrarynumber of children and manages it as a linked list.In some rare cases, more complicated child models are used. TheRenderTable render object’s constructor takes an array of arrays ofchildren, the class exposes getters and setters that control the numberof rows and columns, and there are specific methods to replaceindividual children by x,y coordinate, to add a row, to provide anew array of arrays of children, and to replace the entire child listwith a single array and a column count. In the implementation,the object does not use a linked list like most render objects butinstead uses an indexable array.The Chip widgets and InputDecoration objects have fields that matchthe slots that exist on the relevant controls. Where a one-size-fits-allchild model would force semantics to be layered on top of a list ofchildren, for example, defining the first child to be the prefix valueand the second to be the suffix, the dedicated child model allows fordedicated named properties to be used instead.This flexibility allows each node in these trees to be manipulated inthe way most idiomatic for its role. It’s rare to want to insert a cellin a table, causing all the other cells to wrap around; similarly,it’s rare to want to remove a child from a flex row by index insteadof by reference.The RenderParagraph object is the most extreme case: it has a child ofan entirely different type, TextSpan. At the RenderParagraph boundary,the RenderObject tree transitions into being a TextSpan tree.The overall approach of specializing APIs to meet the developer’sexpectations is applied to more than just child models.Some rather trivial widgets exist specifically so that developerswill find them when looking for a solution to a problem. Adding aspace to a row or column is easily done once one knows how, usingthe Expanded widget and a zero-sized SizedBox child, but discoveringthat pattern is unnecessary because searching for spaceuncovers the Spacer widget, which uses Expanded and SizedBox directlyto achieve the effect.Similarly, hiding a widget subtree is easily done by not including thewidget subtree in the build at all. However, developers typically expectthere to be a widget to do this, and so the Visibility widget existsto wrap this pattern in a trivial reusable widget.<topic_end><topic_start>Explicit argumentsUI frameworks tend to have many properties, such that a developer israrely able to remember the semantic meaning of each constructorargument of each class. As Flutter uses the reactive paradigm,it is common for build methods in Flutter to have many calls toconstructors. By leveraging Dart’s support for named arguments,Flutter’s API is able to keep such build methods clear and understandable.This pattern is extended to any method with multiple arguments,and in particular is extended to any boolean argument, so that isolatedtrue or false literals in method calls are always self-documenting.Furthermore, to avoid confusion commonly caused by double negativesin APIs, boolean arguments and properties are always named in thepositive form (for example, enabled: true rather than disabled: false).<topic_end><topic_start>Paving over pitfallsA technique used in a number of places in the Flutter framework is todefine the API such that error conditions don’t exist. This removesentire classes of errors from consideration.For example, interpolation functions allow one or both ends of theinterpolation to be null, instead of defining that as an error case:interpolating between two null values is always null, and interpolatingfrom a null value or to a null value is the equivalent of interpolatingto the zero analog for the given type. This means that developerswho accidentally pass null to an interpolation function will not hitan error case, but will instead get a reasonable result.A more subtle example is in the Flex layout algorithm. The concept ofthis layout is that the space given to the flex render object isdivided among its children, so the size of the flex should be theentirety of the available space. In the original design, providinginfinite space would fail: it would imply that the flex should beinfinitely sized, a useless layout configuration. Instead, the APIwas adjusted so that when infinite space is allocated to the flexrender object, the render object sizes itself to fit the desiredsize of the children, reducing the possible number of error cases.The approach is also used to avoid having constructors that allowinconsistent data to be created. For instance, the PointerDownEventconstructor does not allow the down property of PointerEvent tobe set to false (a situation that would be self-contradictory);instead, the constructor does not have a parameter for the downfield and always sets it to true.In general, the approach is to define valid interpretations for allvalues in the input domain. The simplest example is the Color constructor.Instead of taking four integers, one for red, one for green,one for blue, and one for alpha, each of which could be out of range,the default constructor takes a single integer value, and definesthe meaning of each bit (for example, the bottom eight bits define thered component), so that any input value is a valid color value.A more elaborate example is the paintImage() function. This functiontakes eleven arguments, some with quite wide input domains, but theyhave been carefully designed to be mostly orthogonal to each other,such that there are very few invalid combinations.<topic_end><topic_start>Reporting error cases aggressivelyNot all error conditions can be designed out. For those that remain,in debug builds, Flutter generally attempts to catch the errors veryearly and immediately reports them. Asserts are widely used.Constructor arguments are sanity checked in detail. Lifecycles aremonitored and when inconsistencies are detected they immediatelycause an exception to be thrown.In some cases, this is taken to extremes: for example, when runningunit tests, regardless of what else the test is doing, every RenderBoxsubclass that is laid out aggressively inspects whether its intrinsicsizing methods fulfill the intrinsic sizing contract. This helps catcherrors in APIs that might otherwise not be exercised.When exceptions are thrown, they include as much information asis available. Some of Flutter’s error messages proactively probe theassociated stack trace to determine the most likely location of theactual bug. Others walk the relevant trees to determine the sourceof bad data. The most common errors include detailed instructionsincluding in some cases sample code for avoiding the error, or linksto further documentation.<topic_end><topic_start>Reactive paradigmMutable tree-based APIs suffer from a dichotomous access pattern:creating the tree’s original state typically uses a very differentset of operations than subsequent updates. Flutter’s rendering layeruses this paradigm, as it is an effective way to maintain a persistent tree,which is key for efficient layout and painting. However, it meansthat direct interaction with the rendering layer is awkward at bestand bug-prone at worst.Flutter’s widget layer introduces a composition mechanism using thereactive paradigm7 to manipulate theunderlying rendering tree.This API abstracts out the tree manipulation by combining the treecreation and tree mutation steps into a single tree description (build)step, where, after each change to the system state, the new configurationof the user interface is described by the developer and the frameworkcomputes the series of tree mutations necessary to reflect this newconfiguration.<topic_end><topic_start>InterpolationSince Flutter’s framework encourages developers to describe the interfaceconfiguration matching the current application state, a mechanism existsto implicitly animate between these configurations.For example, suppose that in state S1 the interface consistsof a circle, but in state S2 it consists of a square.Without an animation mechanism, the state change would have a jarringinterface change. An implicit animation allows the circle to be smoothlysquared over several frames.Each feature that can be implicitly animated has a stateful widget thatkeeps a record of the current value of the input, and begins an animationsequence whenever the input value changes, transitioning from the currentvalue to the new value over a specified duration.This is implemented using lerp (linear interpolation) functions usingimmutable objects. Each state (circle and square, in this case)is represented as an immutable object that is configured withappropriate settings (color, stroke width, etc) and knows how to paintitself. When it is time to draw the intermediate steps during the animation,the start and end values are passed to the appropriate lerp functionalong with a t value representing the point along the animation,where 0.0 represents the start and 1.0 represents theend8,and the function returns a third immutable object representing theintermediate stage.For the circle-to-square transition, the lerp function would returnan object representing a “rounded square” with a radius described asa fraction derived from the t value, a color interpolated using thelerp function for colors, and a stroke width interpolated using thelerp function for doubles. That object, which implements thesame interface as circles and squares, would then be able to paintitself when requested to.This technique allows the state machinery, the mapping of states toconfigurations, the animation machinery, the interpolation machinery,and the specific logic relating to how to paint each frame to beentirely separated from each other.This approach is broadly applicable. In Flutter, basic types likeColor and Shape can be interpolated, but so can much more elaboratetypes such as Decoration, TextStyle, or Theme. These aretypically constructed from components that can themselves be interpolated,and interpolating the more complicated objects is often as simple asrecursively interpolating all the values that describe the complicatedobjects.Some interpolatable objects are defined by class hierarchies. For example,shapes are represented by the ShapeBorder interface, and there exists avariety of shapes, including BeveledRectangleBorder, BoxBorder,CircleBorder, RoundedRectangleBorder, and StadiumBorder. A singlelerp function can’t anticipate all possible types,and therefore the interface instead defines lerpFrom and lerpTo methods,which the static lerp method defers to. When told to interpolate froma shape A to a shape B, first B is asked if it can lerpFrom A, then,if it cannot, A is instead asked if it can lerpTo B. (If neither ispossible, then the function returns A from values of t less than 0.5,and returns B otherwise.)This allows the class hierarchy to be arbitrarily extended, with lateradditions being able to interpolate between previously-known valuesand themselves.In some cases, the interpolation itself cannot be described by any ofthe available classes, and a private class is defined to describe theintermediate stage. This is the case, for instance, when interpolatingbetween a CircleBorder and a RoundedRectangleBorder.This mechanism has one further added advantage: it can handle interpolationfrom intermediate stages to new values. For example, half-way througha circle-to-square transition, the shape could be changed once more,causing the animation to need to interpolate to a triangle. So long asthe triangle class can lerpFrom the rounded-square intermediate class,the transition can be seamlessly performed.<topic_end><topic_start>ConclusionFlutter’s slogan, “everything is a widget,” revolves around buildinguser interfaces by composing widgets that are, in turn, composed ofprogressively more basic widgets. The result of this aggressivecomposition is a large number of widgets that require carefullydesigned algorithms and data structures to process efficiently.With some additional design, these data structures also make iteasy for developers to create infinite scrolling lists that buildwidgets on demand when they become visible.Footnotes:1 For layout, at least. It might be revisited  for painting, for building the accessibility tree if necessary,  and for hit testing if necessary.2 Reality, of course, is a bit more  complicated. Some layouts involve intrinsic dimensions or baseline  measurements, which do involve an additional walk of the relevant subtree  (aggressive caching is used to mitigate the potential for quadratic  performance in the worst case). These cases, however, are surprisingly  rare. In particular, intrinsic dimensions are not required for the  common case of shrink-wrapping.3 Technically, the child’s position is not  part of its RenderBox geometry and therefore need not actually be  calculated during layout. Many render objects implicitly position  their single child at 0,0 relative to their own origin, which  requires no computation or storage at all. Some render objects  avoid computing the position of their children until the last  possible moment (for example, during the paint phase), to avoid  the computation entirely if they are not subsequently painted.4  There exists one exception to this rule.  As discussed in the Building widgets on demand  section, some widgets can be rebuilt as a result of a change in layout  constraints. If a widget marked itself dirty for unrelated reasons in  the same frame that it also is affected by a change in layout constraints,  it will be updated twice. This redundant build is limited to the  widget itself and does not impact its descendants.5 A key is an opaque object optionally  associated with a widget whose equality operator is used to influence  the reconciliation algorithm.6  For accessibility, and to give applications  a few extra milliseconds between when a widget is built and when it  appears on the screen, the viewport creates (but does not paint)  widgets for a few hundred pixels before and after the visible widgets.7  This approach was first made popular by  Facebook’s React library.8  In practice, the t value is allowed  to extend past the 0.0-1.0 range, and does so for some curves. For  example, the “elastic” curves overshoot briefly in order to represent  a bouncing effect. The interpolation logic typically can extrapolate  past the start or end as appropriate. For some types, for example,  when interpolating colors, the t value is effectively clamped to  the 0.0-1.0 range.<topic_end><topic_start>Understanding constraintsinfo Note  If you are experiencing specific layout errors,  you might check out Common Flutter errors.When someone learning Flutter asks you why some widgetwith width: 100 isn’t 100 pixels wide,the default answer is to tell them to put that widgetinside of a Center, right?Don’t do that.If you do, they’ll come back again and again,asking why some FittedBox isn’t working,why that Column is overflowing, or whatIntrinsicWidth is supposed to be doing.Instead, first tell them that Flutter layout is very differentfrom HTML layout (which is probably where they’re coming from),and then make them memorize the following rule:Flutter layout can’t really be understood without knowingthis rule, so Flutter developers should learn it early on.In more detail:For example, if a composed widget contains a columnwith some padding, and wants to lay out its two childrenas follows:The negotiation goes something like this:Widget: “Hey parent, what are my constraints?”Parent: “You must be from 0 to 300 pixels wide,   and 0 to 85 tall.”Widget: “Hmmm, since I want to have 5 pixels of padding,   then my children can have at most 290 pixels of width   and 75 pixels of height.”Widget: “Hey first child, You must be from 0 to 290   pixels wide, and 0 to 75 tall.”First child: “OK, then I wish to be 290 pixels wide,   and 20 pixels tall.”Widget: “Hmmm, since I want to put my second child below the   first one, this leaves only 55 pixels of height for   my second child.”Widget: “Hey second child, You must be from 0 to 290 wide,   and 0 to 55 tall.”Second child: “OK, I wish to be 140 pixels wide,   and 30 pixels tall.”Widget: “Very well. My first child has position x: 5 and y: 5,   and my second child has x: 80 and y: 25.”Widget: “Hey parent, I’ve decided that my size is going to be 300   pixels wide, and 60 pixels tall.”<topic_end><topic_start>LimitationsFlutter’s layout engine is designed to be a one-pass process.This means that Flutter lays out its widgets very efficiently,but does result in a few limitations:A widget can decide its own size only within theconstraints given to it by its parent.This means a widget usuallycan’t have any size it wants.A widget can’t know and doesn’t decide its own positionin the screen, since it’s the widget’s parent who decidesthe position of the widget.Since the parent’s size and position, in its turn,also depends on its own parent, it’s impossible toprecisely define the size and position of any widgetwithout taking into consideration the tree as a whole.If a child wants a different size from its parent andthe parent doesn’t have enough information to align it,then the child’s size might be ignored.Be specific when defining alignment.In Flutter, widgets are rendered by their underlyingRenderBox objects. Many boxes in Flutter,especially those that just take a single child,pass their constraint on to their children.Generally, there are three kinds of boxes,in terms of how they handle their constraints:Some widgets, for example Container,vary from type to type based on their constructor arguments.The Container constructor defaultsto trying to be as big as possible, but if you give it a width,for instance, it tries to honor that and be that particular size.Others, for example Row and Column (flex boxes)vary based on the constraints they are given,as described in the Flex section.<topic_end><topic_start>ExamplesFor an interactive experience, use the following DartPad.Use the numbered horizontal scrolling bar to switch between29 different examples.<code_start>import 'package:flutter/material.dart';void main() => runApp(const HomePage());const red = Colors.red;const green = Colors.green;const blue = Colors.blue;const big = TextStyle(fontSize: 30);//////////////////////////////////////////////////class HomePage extends StatelessWidget {  const HomePage({super.key});  @override  Widget build(BuildContext context) {    return const FlutterLayoutArticle([      Example1(),      Example2(),      Example3(),      Example4(),      Example5(),      Example6(),      Example7(),      Example8(),      Example9(),      Example10(),      Example11(),      Example12(),      Example13(),      Example14(),      Example15(),      Example16(),      Example17(),      Example18(),      Example19(),      Example20(),      Example21(),      Example22(),      Example23(),      Example24(),      Example25(),      Example26(),      Example27(),      Example28(),      Example29(),    ]);  }}//////////////////////////////////////////////////abstract class Example extends StatelessWidget {  const Example({super.key});  String get code;  String get explanation;}//////////////////////////////////////////////////class FlutterLayoutArticle extends StatefulWidget {  const FlutterLayoutArticle(    this.examples, {    super.key,  });  final List<Example> examples;  @override  State<FlutterLayoutArticle> createState() => _FlutterLayoutArticleState();}//////////////////////////////////////////////////class _FlutterLayoutArticleState extends State<FlutterLayoutArticle> {  late int count;  late Widget example;  late String code;  late String explanation;  @override  void initState() {    count = 1;    code = const Example1().code;    explanation = const Example1().explanation;    super.initState();  }  @override  void didUpdateWidget(FlutterLayoutArticle oldWidget) {    super.didUpdateWidget(oldWidget);    var example = widget.examples[count - 1];    code = example.code;    explanation = example.explanation;  }  @override  Widget build(BuildContext context) {    return MaterialApp(      debugShowCheckedModeBanner: false,      title: 'Flutter Layout Article',      home: SafeArea(        child: Material(          color: Colors.black,          child: FittedBox(            child: Container(              width: 400,              height: 670,              color: const Color(0xFFCCCCCC),              child: Column(                crossAxisAlignment: CrossAxisAlignment.center,                children: [                  Expanded(                      child: ConstrainedBox(                          constraints: const BoxConstraints.tightFor(                              width: double.infinity, height: double.infinity),                          child: widget.examples[count - 1])),                  Container(                    height: 50,                    width: double.infinity,                    color: Colors.black,                    child: SingleChildScrollView(                      scrollDirection: Axis.horizontal,                      child: Row(                        mainAxisSize: MainAxisSize.min,                        children: [                          for (int i = 0; i < widget.examples.length; i++)                            Container(                              width: 58,                              padding: const EdgeInsets.only(left: 4, right: 4),                              child: button(i + 1),                            ),                        ],                      ),                    ),                  ),                  Container(                    height: 273,                    color: Colors.grey[50],                    child: Scrollbar(                      child: SingleChildScrollView(                        key: ValueKey(count),                        child: Padding(                          padding: const EdgeInsets.all(10),                          child: Column(                            children: [                              Center(child: Text(code)),                              const SizedBox(height: 15),                              Text(                                explanation,                                style: TextStyle(                                    color: Colors.blue[900],                                    fontStyle: FontStyle.italic),                              ),                            ],                          ),                        ),                      ),                    ),                  ),                ],              ),            ),          ),        ),      ),    );  }  Widget button(int exampleNumber) {    return Button(      key: ValueKey('button$exampleNumber'),      isSelected: count == exampleNumber,      exampleNumber: exampleNumber,      onPressed: () {        showExample(          exampleNumber,          widget.examples[exampleNumber - 1].code,          widget.examples[exampleNumber - 1].explanation,        );      },    );  }  void showExample(int exampleNumber, String code, String explanation) {    setState(() {      count = exampleNumber;      this.code = code;      this.explanation = explanation;    });  }}//////////////////////////////////////////////////class Button extends StatelessWidget {  final bool isSelected;  final int exampleNumber;  final VoidCallback onPressed;  const Button({    super.key,    required this.isSelected,    required this.exampleNumber,    required this.onPressed,  });  @override  Widget build(BuildContext context) {    return TextButton(      style: TextButton.styleFrom(        foregroundColor: Colors.white,        backgroundColor: isSelected ? Colors.grey : Colors.grey[800],      ),      child: Text(exampleNumber.toString()),      onPressed: () {        Scrollable.ensureVisible(          context,          duration: const Duration(milliseconds: 350),          curve: Curves.easeOut,          alignment: 0.5,        );        onPressed();      },    );  }}//////////////////////////////////////////////////class Example1 extends Example {  const Example1({super.key});  @override  final code = 'Container(color: red)';  @override  final explanation = 'The screen is the parent of the Container, '      'and it forces the Container to be exactly the same size as the screen.'      '\n\n'      'So the Container fills the screen and paints it red.';  @override  Widget build(BuildContext context) {    return Container(color: red);  }}//////////////////////////////////////////////////class Example2 extends Example {  const Example2({super.key});  @override  final code = 'Container(width: 100, height: 100, color: red)';  @override  final String explanation =      'The red Container wants to be 100x100, but it can\'t, '      'because the screen forces it to be exactly the same size as the screen.'      '\n\n'      'So the Container fills the screen.';  @override  Widget build(BuildContext context) {    return Container(width: 100, height: 100, color: red);  }}//////////////////////////////////////////////////class Example3 extends Example {  const Example3({super.key});  @override  final code = 'Center(\n'      '   child: Container(width: 100, height: 100, color: red))';  @override  final String explanation =      'The screen forces the Center to be exactly the same size as the screen, '      'so the Center fills the screen.'      '\n\n'      'The Center tells the Container that it can be any size it wants, but not bigger than the screen.'      'Now the Container can indeed be 100x100.';  @override  Widget build(BuildContext context) {    return Center(      child: Container(width: 100, height: 100, color: red),    );  }}//////////////////////////////////////////////////class Example4 extends Example {  const Example4({super.key});  @override  final code = 'Align(\n'      '   alignment: Alignment.bottomRight,\n'      '   child: Container(width: 100, height: 100, color: red))';  @override  final String explanation =      'This is different from the previous example in that it uses Align instead of Center.'      '\n\n'      'Align also tells the Container that it can be any size it wants, but if there is empty space it won\'t center the Container. '      'Instead, it aligns the Container to the bottom-right of the available space.';  @override  Widget build(BuildContext context) {    return Align(      alignment: Alignment.bottomRight,      child: Container(width: 100, height: 100, color: red),    );  }}//////////////////////////////////////////////////class Example5 extends Example {  const Example5({super.key});  @override  final code = 'Center(\n'      '   child: Container(\n'      '              color: red,\n'      '              width: double.infinity,\n'      '              height: double.infinity))';  @override  final String explanation =      'The screen forces the Center to be exactly the same size as the screen, '      'so the Center fills the screen.'      '\n\n'      'The Center tells the Container that it can be any size it wants, but not bigger than the screen.'      'The Container wants to be of infinite size, but since it can\'t be bigger than the screen, it just fills the screen.';  @override  Widget build(BuildContext context) {    return Center(      child: Container(          width: double.infinity, height: double.infinity, color: red),    );  }}//////////////////////////////////////////////////class Example6 extends Example {  const Example6({super.key});  @override  final code = 'Center(child: Container(color: red))';  @override  final String explanation =      'The screen forces the Center to be exactly the same size as the screen, '      'so the Center fills the screen.'      '\n\n'      'The Center tells the Container that it can be any size it wants, but not bigger than the screen.'      '\n\n'      'Since the Container has no child and no fixed size, it decides it wants to be as big as possible, so it fills the whole screen.'      '\n\n'      'But why does the Container decide that? '      'Simply because that\'s a design decision by those who created the Container widget. '      'It could have been created differently, and you have to read the Container documentation to understand how it behaves, depending on the circumstances. ';  @override  Widget build(BuildContext context) {    return Center(      child: Container(color: red),    );  }}//////////////////////////////////////////////////class Example7 extends Example {  const Example7({super.key});  @override  final code = 'Center(\n'      '   child: Container(color: red\n'      '      child: Container(color: green, width: 30, height: 30)))';  @override  final String explanation =      'The screen forces the Center to be exactly the same size as the screen, '      'so the Center fills the screen.'      '\n\n'      'The Center tells the red Container that it can be any size it wants, but not bigger than the screen.'      'Since the red Container has no size but has a child, it decides it wants to be the same size as its child.'      '\n\n'      'The red Container tells its child that it can be any size it wants, but not bigger than the screen.'      '\n\n'      'The child is a green Container that wants to be 30x30.'      '\n\n'      'Since the red `Container` has no size but has a child, it decides it wants to be the same size as its child. '      'The red color isn\'t visible, since the green Container entirely covers all of the red Container.';  @override  Widget build(BuildContext context) {    return Center(      child: Container(        color: red,        child: Container(color: green, width: 30, height: 30),      ),    );  }}//////////////////////////////////////////////////class Example8 extends Example {  const Example8({super.key});  @override  final code = 'Center(\n'      '   child: Container(color: red\n'      '      padding: const EdgeInsets.all(20),\n'      '      child: Container(color: green, width: 30, height: 30)))';  @override  final String explanation =      'The red Container sizes itself to its children size, but it takes its own padding into consideration. '      'So it is also 30x30 plus padding. '      'The red color is visible because of the padding, and the green Container has the same size as in the previous example.';  @override  Widget build(BuildContext context) {    return Center(      child: Container(        padding: const EdgeInsets.all(20),        color: red,        child: Container(color: green, width: 30, height: 30),      ),    );  }}//////////////////////////////////////////////////class Example9 extends Example {  const Example9({super.key});  @override  final code = 'ConstrainedBox(\n'      '   constraints: BoxConstraints(\n'      '              minWidth: 70, minHeight: 70,\n'      '              maxWidth: 150, maxHeight: 150),\n'      '      child: Container(color: red, width: 10, height: 10)))';  @override  final String explanation =      'You might guess that the Container has to be between 70 and 150 pixels, but you would be wrong. '      'The ConstrainedBox only imposes ADDITIONAL constraints from those it receives from its parent.'      '\n\n'      'Here, the screen forces the ConstrainedBox to be exactly the same size as the screen, '      'so it tells its child Container to also assume the size of the screen, '      'thus ignoring its \'constraints\' parameter.';  @override  Widget build(BuildContext context) {    return ConstrainedBox(      constraints: const BoxConstraints(        minWidth: 70,        minHeight: 70,        maxWidth: 150,        maxHeight: 150,      ),      child: Container(color: red, width: 10, height: 10),    );  }}//////////////////////////////////////////////////class Example10 extends Example {  const Example10({super.key});  @override  final code = 'Center(\n'      '   child: ConstrainedBox(\n'      '      constraints: BoxConstraints(\n'      '                 minWidth: 70, minHeight: 70,\n'      '                 maxWidth: 150, maxHeight: 150),\n'      '        child: Container(color: red, width: 10, height: 10))))';  @override  final String explanation =      'Now, Center allows ConstrainedBox to be any size up to the screen size.'      '\n\n'      'The ConstrainedBox imposes ADDITIONAL constraints from its \'constraints\' parameter onto its child.'      '\n\n'      'The Container must be between 70 and 150 pixels. It wants to have 10 pixels, so it will end up having 70 (the MINIMUM).';  @override  Widget build(BuildContext context) {    return Center(      child: ConstrainedBox(        constraints: const BoxConstraints(          minWidth: 70,          minHeight: 70,          maxWidth: 150,          maxHeight: 150,        ),        child: Container(color: red, width: 10, height: 10),      ),    );  }}//////////////////////////////////////////////////class Example11 extends Example {  const Example11({super.key});  @override  final code = 'Center(\n'      '   child: ConstrainedBox(\n'      '      constraints: BoxConstraints(\n'      '                 minWidth: 70, minHeight: 70,\n'      '                 maxWidth: 150, maxHeight: 150),\n'      '        child: Container(color: red, width: 1000, height: 1000))))';  @override  final String explanation =      'Center allows ConstrainedBox to be any size up to the screen size.'      'The ConstrainedBox imposes ADDITIONAL constraints from its \'constraints\' parameter onto its child'      '\n\n'      'The Container must be between 70 and 150 pixels. It wants to have 1000 pixels, so it ends up having 150 (the MAXIMUM).';  @override  Widget build(BuildContext context) {    return Center(      child: ConstrainedBox(        constraints: const BoxConstraints(          minWidth: 70,          minHeight: 70,          maxWidth: 150,          maxHeight: 150,        ),        child: Container(color: red, width: 1000, height: 1000),      ),    );  }}//////////////////////////////////////////////////class Example12 extends Example {  const Example12({super.key});  @override  final code = 'Center(\n'      '   child: ConstrainedBox(\n'      '      constraints: BoxConstraints(\n'      '                 minWidth: 70, minHeight: 70,\n'      '                 maxWidth: 150, maxHeight: 150),\n'      '        child: Container(color: red, width: 100, height: 100))))';  @override  final String explanation =      'Center allows ConstrainedBox to be any size up to the screen size.'      'ConstrainedBox imposes ADDITIONAL constraints from its \'constraints\' parameter onto its child.'      '\n\n'      'The Container must be between 70 and 150 pixels. It wants to have 100 pixels, and that\'s the size it has, since that\'s between 70 and 150.';  @override  Widget build(BuildContext context) {    return Center(      child: ConstrainedBox(        constraints: const BoxConstraints(          minWidth: 70,          minHeight: 70,          maxWidth: 150,          maxHeight: 150,        ),        child: Container(color: red, width: 100, height: 100),      ),    );  }}//////////////////////////////////////////////////class Example13 extends Example {  const Example13({super.key});  @override  final code = 'UnconstrainedBox(\n'      '   child: Container(color: red, width: 20, height: 50));';  @override  final String explanation =      'The screen forces the UnconstrainedBox to be exactly the same size as the screen.'      'However, the UnconstrainedBox lets its child Container be any size it wants.';  @override  Widget build(BuildContext context) {    return UnconstrainedBox(      child: Container(color: red, width: 20, height: 50),    );  }}//////////////////////////////////////////////////class Example14 extends Example {  const Example14({super.key});  @override  final code = 'UnconstrainedBox(\n'      '   child: Container(color: red, width: 4000, height: 50));';  @override  final String explanation =      'The screen forces the UnconstrainedBox to be exactly the same size as the screen, '      'and UnconstrainedBox lets its child Container be any size it wants.'      '\n\n'      'Unfortunately, in this case the Container has 4000 pixels of width and is too big to fit in the UnconstrainedBox, '      'so the UnconstrainedBox displays the much dreaded "overflow warning".';  @override  Widget build(BuildContext context) {    return UnconstrainedBox(      child: Container(color: red, width: 4000, height: 50),    );  }}//////////////////////////////////////////////////class Example15 extends Example {  const Example15({super.key});  @override  final code = 'OverflowBox(\n'      '   minWidth: 0,'      '   minHeight: 0,'      '   maxWidth: double.infinity,'      '   maxHeight: double.infinity,'      '   child: Container(color: red, width: 4000, height: 50));';  @override  final String explanation =      'The screen forces the OverflowBox to be exactly the same size as the screen, '      'and OverflowBox lets its child Container be any size it wants.'      '\n\n'      'OverflowBox is similar to UnconstrainedBox, and the difference is that it won\'t display any warnings if the child doesn\'t fit the space.'      '\n\n'      'In this case the Container is 4000 pixels wide, and is too big to fit in the OverflowBox, '      'but the OverflowBox simply shows as much as it can, with no warnings given.';  @override  Widget build(BuildContext context) {    return OverflowBox(      minWidth: 0,      minHeight: 0,      maxWidth: double.infinity,      maxHeight: double.infinity,      child: Container(color: red, width: 4000, height: 50),    );  }}//////////////////////////////////////////////////class Example16 extends Example {  const Example16({super.key});  @override  final code = 'UnconstrainedBox(\n'      '   child: Container(color: Colors.red, width: double.infinity, height: 100));';  @override  final String explanation =      'This won\'t render anything, and you\'ll see an error in the console.'      '\n\n'      'The UnconstrainedBox lets its child be any size it wants, '      'however its child is a Container with infinite size.'      '\n\n'      'Flutter can\'t render infinite sizes, so it throws an error with the following message: '      '"BoxConstraints forces an infinite width."';  @override  Widget build(BuildContext context) {    return UnconstrainedBox(      child: Container(color: Colors.red, width: double.infinity, height: 100),    );  }}//////////////////////////////////////////////////class Example17 extends Example {  const Example17({super.key});  @override  final code = 'UnconstrainedBox(\n'      '   child: LimitedBox(maxWidth: 100,\n'      '      child: Container(color: Colors.red,\n'      '                       width: double.infinity, height: 100));';  @override  final String explanation = 'Here you won\'t get an error anymore, '      'because when the LimitedBox is given an infinite size by the UnconstrainedBox, '      'it passes a maximum width of 100 down to its child.'      '\n\n'      'If you swap the UnconstrainedBox for a Center widget, '      'the LimitedBox won\'t apply its limit anymore (since its limit is only applied when it gets infinite constraints), '      'and the width of the Container is allowed to grow past 100.'      '\n\n'      'This explains the difference between a LimitedBox and a ConstrainedBox.';  @override  Widget build(BuildContext context) {    return UnconstrainedBox(      child: LimitedBox(        maxWidth: 100,        child: Container(          color: Colors.red,          width: double.infinity,          height: 100,        ),      ),    );  }}//////////////////////////////////////////////////class Example18 extends Example {  const Example18({super.key});  @override  final code = 'FittedBox(\n'      '   child: Text(\'Some Example Text.\'));';  @override  final String explanation =      'The screen forces the FittedBox to be exactly the same size as the screen.'      'The Text has some natural width (also called its intrinsic width) that depends on the amount of text, its font size, and so on.'      '\n\n'      'The FittedBox lets the Text be any size it wants, '      'but after the Text tells its size to the FittedBox, '      'the FittedBox scales the Text until it fills all of the available width.';  @override  Widget build(BuildContext context) {    return const FittedBox(      child: Text('Some Example Text.'),    );  }}//////////////////////////////////////////////////class Example19 extends Example {  const Example19({super.key});  @override  final code = 'Center(\n'      '   child: FittedBox(\n'      '      child: Text(\'Some Example Text.\')));';  @override  final String explanation =      'But what happens if you put the FittedBox inside of a Center widget? '      'The Center lets the FittedBox be any size it wants, up to the screen size.'      '\n\n'      'The FittedBox then sizes itself to the Text, and lets the Text be any size it wants.'      '\n\n'      'Since both FittedBox and the Text have the same size, no scaling happens.';  @override  Widget build(BuildContext context) {    return const Center(      child: FittedBox(        child: Text('Some Example Text.'),      ),    );  }}////////////////////////////////////////////////////class Example20 extends Example {  const Example20({super.key});  @override  final code = 'Center(\n'      '   child: FittedBox(\n'      '      child: Text(\'…\')));';  @override  final String explanation =      'However, what happens if FittedBox is inside of a Center widget, but the Text is too large to fit the screen?'      '\n\n'      'FittedBox tries to size itself to the Text, but it can\'t be bigger than the screen. '      'It then assumes the screen size, and resizes Text so that it fits the screen, too.';  @override  Widget build(BuildContext context) {    return const Center(      child: FittedBox(        child: Text(            'This is some very very very large text that is too big to fit a regular screen in a single line.'),      ),    );  }}//////////////////////////////////////////////////class Example21 extends Example {  const Example21({super.key});  @override  final code = 'Center(\n'      '   child: Text(\'…\'));';  @override  final String explanation = 'If, however, you remove the FittedBox, '      'the Text gets its maximum width from the screen, '      'and breaks the line so that it fits the screen.';  @override  Widget build(BuildContext context) {    return const Center(      child: Text(          'This is some very very very large text that is too big to fit a regular screen in a single line.'),    );  }}//////////////////////////////////////////////////class Example22 extends Example {  const Example22({super.key});  @override  final code = 'FittedBox(\n'      '   child: Container(\n'      '      height: 20, width: double.infinity));';  @override  final String explanation =      'FittedBox can only scale a widget that is BOUNDED (has non-infinite width and height).'      'Otherwise, it won\'t render anything, and you\'ll see an error in the console.';  @override  Widget build(BuildContext context) {    return FittedBox(      child: Container(        height: 20,        width: double.infinity,        color: Colors.red,      ),    );  }}//////////////////////////////////////////////////class Example23 extends Example {  const Example23({super.key});  @override  final code = 'Row(children:[\n'      '   Container(color: red, child: Text(\'Hello!\'))\n'      '   Container(color: green, child: Text(\'Goodbye!\'))]';  @override  final String explanation =      'The screen forces the Row to be exactly the same size as the screen.'      '\n\n'      'Just like an UnconstrainedBox, the Row won\'t impose any constraints onto its children, '      'and instead lets them be any size they want.'      '\n\n'      'The Row then puts them side-by-side, and any extra space remains empty.';  @override  Widget build(BuildContext context) {    return Row(      children: [        Container(color: red, child: const Text('Hello!', style: big)),        Container(color: green, child: const Text('Goodbye!', style: big)),      ],    );  }}//////////////////////////////////////////////////class Example24 extends Example {  const Example24({super.key});  @override  final code = 'Row(children:[\n'      '   Container(color: red, child: Text(\'…\'))\n'      '   Container(color: green, child: Text(\'Goodbye!\'))]';  @override  final String explanation =      'Since the Row won\'t impose any constraints onto its children, '      'it\'s quite possible that the children might be too big to fit the available width of the Row.'      'In this case, just like an UnconstrainedBox, the Row displays the "overflow warning".';  @override  Widget build(BuildContext context) {    return Row(      children: [        Container(          color: red,          child: const Text(            'This is a very long text that '            'won\'t fit the line.',            style: big,          ),        ),        Container(color: green, child: const Text('Goodbye!', style: big)),      ],    );  }}//////////////////////////////////////////////////class Example25 extends Example {  const Example25({super.key});  @override  final code = 'Row(children:[\n'      '   Expanded(\n'      '       child: Container(color: red, child: Text(\'…\')))\n'      '   Container(color: green, child: Text(\'Goodbye!\'))]';  @override  final String explanation =      'When a Row\'s child is wrapped in an Expanded widget, the Row won\'t let this child define its own width anymore.'      '\n\n'      'Instead, it defines the Expanded width according to the other children, and only then the Expanded widget forces the original child to have the Expanded\'s width.'      '\n\n'      'In other words, once you use Expanded, the original child\'s width becomes irrelevant, and is ignored.';  @override  Widget build(BuildContext context) {    return Row(      children: [        Expanded(          child: Center(            child: Container(              color: red,              child: const Text(                'This is a very long text that won\'t fit the line.',                style: big,              ),            ),          ),        ),        Container(color: green, child: const Text('Goodbye!', style: big)),      ],    );  }}//////////////////////////////////////////////////class Example26 extends Example {  const Example26({super.key});  @override  final code = 'Row(children:[\n'      '   Expanded(\n'      '       child: Container(color: red, child: Text(\'…\')))\n'      '   Expanded(\n'      '       child: Container(color: green, child: Text(\'Goodbye!\'))]';  @override  final String explanation =      'If all of Row\'s children are wrapped in Expanded widgets, each Expanded has a size proportional to its flex parameter, '      'and only then each Expanded widget forces its child to have the Expanded\'s width.'      '\n\n'      'In other words, Expanded ignores the preferred width of its children.';  @override  Widget build(BuildContext context) {    return Row(      children: [        Expanded(          child: Container(            color: red,            child: const Text(              'This is a very long text that won\'t fit the line.',              style: big,            ),          ),        ),        Expanded(          child: Container(            color: green,            child: const Text(              'Goodbye!',              style: big,            ),          ),        ),      ],    );  }}//////////////////////////////////////////////////class Example27 extends Example {  const Example27({super.key});  @override  final code = 'Row(children:[\n'      '   Flexible(\n'      '       child: Container(color: red, child: Text(\'…\')))\n'      '   Flexible(\n'      '       child: Container(color: green, child: Text(\'Goodbye!\'))]';  @override  final String explanation =      'The only difference if you use Flexible instead of Expanded, '      'is that Flexible lets its child be SMALLER than the Flexible width, '      'while Expanded forces its child to have the same width of the Expanded.'      '\n\n'      'But both Expanded and Flexible ignore their children\'s width when sizing themselves.'      '\n\n'      'This means that it\'s IMPOSSIBLE to expand Row children proportionally to their sizes. '      'The Row either uses the exact child\'s width, or ignores it completely when you use Expanded or Flexible.';  @override  Widget build(BuildContext context) {    return Row(      children: [        Flexible(          child: Container(            color: red,            child: const Text(              'This is a very long text that won\'t fit the line.',              style: big,            ),          ),        ),        Flexible(          child: Container(            color: green,            child: const Text(              'Goodbye!',              style: big,            ),          ),        ),      ],    );  }}//////////////////////////////////////////////////class Example28 extends Example {  const Example28({super.key});  @override  final code = 'Scaffold(\n'      '   body: Container(color: blue,\n'      '   child: Column(\n'      '      children: [\n'      '         Text(\'Hello!\'),\n'      '         Text(\'Goodbye!\')])))';  @override  final String explanation =      'The screen forces the Scaffold to be exactly the same size as the screen, '      'so the Scaffold fills the screen.'      '\n\n'      'The Scaffold tells the Container that it can be any size it wants, but not bigger than the screen.'      '\n\n'      'When a widget tells its child that it can be smaller than a certain size, '      'we say the widget supplies "loose" constraints to its child. More on that later.';  @override  Widget build(BuildContext context) {    return Scaffold(      body: Container(        color: blue,        child: const Column(          children: [            Text('Hello!'),            Text('Goodbye!'),          ],        ),      ),    );  }}//////////////////////////////////////////////////class Example29 extends Example {  const Example29({super.key});  @override  final code = 'Scaffold(\n'      '   body: Container(color: blue,\n'      '   child: SizedBox.expand(\n'      '      child: Column(\n'      '         children: [\n'      '            Text(\'Hello!\'),\n'      '            Text(\'Goodbye!\')]))))';  @override  final String explanation =      'If you want the Scaffold\'s child to be exactly the same size as the Scaffold itself, '      'you can wrap its child with SizedBox.expand.'      '\n\n'      'When a widget tells its child that it must be of a certain size, '      'we say the widget supplies "tight" constraints to its child. More on that later.';  @override  Widget build(BuildContext context) {    return Scaffold(      body: SizedBox.expand(        child: Container(          color: blue,          child: const Column(            children: [              Text('Hello!'),              Text('Goodbye!'),            ],          ),        ),      ),    );  }}//////////////////////////////////////////////////<code_end>If you prefer, you can grab the code fromthis GitHub repo.The examples are explained in the following sections.<topic_end><topic_start>Example 1<code_start>Container(color: red)<code_end>The screen is the parent of the Container, and itforces the Container to be exactly the same size as the screen.So the Container fills the screen and paints it red.<topic_end><topic_start>Example 2<code_start>Container(width: 100, height: 100, color: red)<code_end>The red Container wants to be 100 × 100,but it can’t, because the screen forces it to beexactly the same size as the screen.So the Container fills the screen.<topic_end><topic_start>Example 3<code_start>Center(  child: Container(width: 100, height: 100, color: red),)<code_end>The screen forces the Center to be exactly the same sizeas the screen, so the Center fills the screen.The Center tells the Container that it can be any size itwants, but not bigger than the screen. Now the Containercan indeed be 100 × 100.<topic_end><topic_start>Example 4<code_start>Align(  alignment: Alignment.bottomRight,  child: Container(width: 100, height: 100, color: red),)<code_end>This is different from the previous example in that it usesAlign instead of Center.Align also tells the Container that it can be any size itwants, but if there is empty space it won’t center the Container.Instead, it aligns the container to the bottom-right of theavailable space.<topic_end><topic_start>Example 5<code_start>Center(  child: Container(      width: double.infinity, height: double.infinity, color: red),)<code_end>The screen forces the Center to be exactly thesame size as the screen, so the Center fills the screen.The Center tells the Container that it can be any size it wants,but not bigger than the screen. The Container wants to beof infinite size, but since it can’t be bigger than the screen,it just fills the screen.<topic_end><topic_start>Example 6<code_start>Center(  child: Container(color: red),)<code_end>The screen forces the Center to be exactly thesame size as the screen, so the Center fills the screen.The Center tells the Container that it can be anysize it wants, but not bigger than the screen.Since the Container has no child and no fixed size,it decides it wants to be as big as possible,so it fills the whole screen.But why does the Container decide that?Simply because that’s a design decision by those whocreated the Container widget. It could have beencreated differently, and you have to read theContainer API documentation to understandhow it behaves, depending on the circumstances.<topic_end><topic_start>Example 7<code_start>Center(  child: Container(    color: red,    child: Container(color: green, width: 30, height: 30),  ),)<code_end>The screen forces the Center to be exactly the samesize as the screen, so the Center fills the screen.The Center tells the red Container that it can be any sizeit wants, but not bigger than the screen. Since the redContainer has no size but has a child,it decides it wants to be the same size as its child.The red Container tells its child that it can be any sizeit wants, but not bigger than the screen.The child is a green Container that wants tobe 30 × 30. Given that the red Container sizes itself tothe size of its child, it is also 30 × 30.The red color isn’t visible because the green Containerentirely covers the red Container.<topic_end><topic_start>Example 8<code_start>Center(  child: Container(    padding: const EdgeInsets.all(20),    color: red,    child: Container(color: green, width: 30, height: 30),  ),)<code_end>The red Container sizes itself to its children’s size,but it takes its own padding into consideration.So it is also 30 × 30 plus padding.The red color is visible because of the padding,and the green Container has the same size asin the previous example.<topic_end><topic_start>Example 9<code_start>ConstrainedBox(  constraints: const BoxConstraints(    minWidth: 70,    minHeight: 70,    maxWidth: 150,    maxHeight: 150,  ),  child: Container(color: red, width: 10, height: 10),)<code_end>You might guess that the Container has to bebetween 70 and 150 pixels, but you would be wrong.The ConstrainedBox only imposes additional constraintsfrom those it receives from its parent.Here, the screen forces the ConstrainedBox to be exactlythe same size as the screen, so it tells its child Containerto also assume the size of the screen, thus ignoring itsconstraints parameter.<topic_end><topic_start>Example 10<code_start>Center(  child: ConstrainedBox(    constraints: const BoxConstraints(      minWidth: 70,      minHeight: 70,      maxWidth: 150,      maxHeight: 150,    ),    child: Container(color: red, width: 10, height: 10),  ),)<code_end>Now, Center allows ConstrainedBox to be any size up tothe screen size. The ConstrainedBox imposes additionalconstraints from its constraints parameter onto its child.The Container must be between 70 and 150 pixels.It wants to have 10 pixels,so it ends up having 70 (the minimum).<topic_end><topic_start>Example 11<code_start>Center(  child: ConstrainedBox(    constraints: const BoxConstraints(      minWidth: 70,      minHeight: 70,      maxWidth: 150,      maxHeight: 150,    ),    child: Container(color: red, width: 1000, height: 1000),  ),)<code_end>Center allows ConstrainedBox to be any size up to thescreen size. The ConstrainedBox imposes additionalconstraints from its constraints parameter onto its child.The Container must be between 70 and 150 pixels.It wants to have 1000 pixels,so it ends up having 150 (the maximum).<topic_end><topic_start>Example 12<code_start>Center(  child: ConstrainedBox(    constraints: const BoxConstraints(      minWidth: 70,      minHeight: 70,      maxWidth: 150,      maxHeight: 150,    ),    child: Container(color: red, width: 100, height: 100),  ),)<code_end>Center allows ConstrainedBox to be any size up to thescreen size. The ConstrainedBox imposes additionalconstraints from its constraints parameter onto its child.The Container must be between 70 and 150 pixels.It wants to have 100 pixels, and that’s the size it has,since that’s between 70 and 150.<topic_end><topic_start>Example 13<code_start>UnconstrainedBox(  child: Container(color: red, width: 20, height: 50),)<code_end>The screen forces the UnconstrainedBox to be exactlythe same size as the screen. However, the UnconstrainedBoxlets its child Container be any size it wants.<topic_end><topic_start>Example 14<code_start>UnconstrainedBox(  child: Container(color: red, width: 4000, height: 50),)<code_end>The screen forces the UnconstrainedBox to be exactlythe same size as the screen, and UnconstrainedBoxlets its child Container be any size it wants.Unfortunately, in this case the Container is4000 pixels wide and is too big to fit inthe UnconstrainedBox, so the UnconstrainedBox displaysthe much dreaded “overflow warning”.<topic_end><topic_start>Example 15<code_start>OverflowBox(  minWidth: 0,  minHeight: 0,  maxWidth: double.infinity,  maxHeight: double.infinity,  child: Container(color: red, width: 4000, height: 50),)<code_end>The screen forces the OverflowBox to be exactly the samesize as the screen, and OverflowBox lets its child Containerbe any size it wants.OverflowBox is similar to UnconstrainedBox;the difference is that it won’t display any warningsif the child doesn’t fit the space.In this case, the Container has 4000 pixels of width,and is too big to fit in the OverflowBox,but the OverflowBox simply shows as much as it can,with no warnings given.<topic_end><topic_start>Example 16<code_start>UnconstrainedBox(  child: Container(color: Colors.red, width: double.infinity, height: 100),)<code_end>This won’t render anything, and you’ll see an error in the console.The UnconstrainedBox lets its child be any size it wants,however its child is a Container with infinite size.Flutter can’t render infinite sizes, so it throws an error withthe following message: BoxConstraints forces an infinite width.<topic_end><topic_start>Example 17<code_start>UnconstrainedBox(  child: LimitedBox(    maxWidth: 100,    child: Container(      color: Colors.red,      width: double.infinity,      height: 100,    ),  ),)<code_end>Here you won’t get an error anymore,because when the LimitedBox is given aninfinite size by the UnconstrainedBox;it passes a maximum width of 100 down to its child.If you swap the UnconstrainedBox for a Center widget,the LimitedBox won’t apply its limit anymore(since its limit is only applied when it gets infiniteconstraints), and the width of the Containeris allowed to grow past 100.This explains the difference between a LimitedBoxand a ConstrainedBox.<topic_end><topic_start>Example 18<code_start>const FittedBox(  child: Text('Some Example Text.'),)<code_end>The screen forces the FittedBox to be exactly the samesize as the screen. The Text has some natural width(also called its intrinsic width) that depends on theamount of text, its font size, and so on.The FittedBox lets the Text be any size it wants,but after the Text tells its size to the FittedBox,the FittedBox scales the Text until it fills all ofthe available width.<topic_end><topic_start>Example 19<code_start>const Center(  child: FittedBox(    child: Text('Some Example Text.'),  ),)<code_end>But what happens if you put the FittedBox inside of aCenter widget? The Center lets the FittedBoxbe any size it wants, up to the screen size.The FittedBox then sizes itself to the Text,and lets the Text be any size it wants.Since both FittedBox and the Text have the same size,no scaling happens.<topic_end><topic_start>Example 20<code_start>const Center(  child: FittedBox(    child: Text(        'This is some very very very large text that is too big to fit a regular screen in a single line.'),  ),)<code_end>However, what happens if FittedBox is inside of a Centerwidget, but the Text is too large to fit the screen?FittedBox tries to size itself to the Text,but it can’t be bigger than the screen.It then assumes the screen size,and resizes Text so that it fits the screen, too.<topic_end><topic_start>Example 21<code_start>const Center(  child: Text(      'This is some very very very large text that is too big to fit a regular screen in a single line.'),)<code_end>If, however, you remove the FittedBox, the Textgets its maximum width from the screen,and breaks the line so that it fits the screen.<topic_end><topic_start>Example 22<code_start>FittedBox(  child: Container(    height: 20,    width: double.infinity,    color: Colors.red,  ),)<code_end>FittedBox can only scale a widget that is bounded(has non-infinite width and height). Otherwise,it won’t render anything,and you’ll see an error in the console.<topic_end><topic_start>Example 23<code_start>Row(  children: [    Container(color: red, child: const Text('Hello!', style: big)),    Container(color: green, child: const Text('Goodbye!', style: big)),  ],)<code_end>The screen forces the Row to be exactly the same sizeas the screen.Just like an UnconstrainedBox, the Row won’timpose any constraints onto its children,and instead lets them be any size they want.The Row then puts them side-by-side,and any extra space remains empty.<topic_end><topic_start>Example 24<code_start>Row(  children: [    Container(      color: red,      child: const Text(        'This is a very long text that '        'won\'t fit the line.',        style: big,      ),    ),    Container(color: green, child: const Text('Goodbye!', style: big)),  ],)<code_end>Since Row won’t impose any constraints onto its children,it’s quite possible that the children might be too big to fitthe available width of the Row. In this case, just like anUnconstrainedBox, the Row displays the “overflow warning”.<topic_end><topic_start>Example 25<code_start>Row(  children: [    Expanded(      child: Center(        child: Container(          color: red,          child: const Text(            'This is a very long text that won\'t fit the line.',            style: big,          ),        ),      ),    ),    Container(color: green, child: const Text('Goodbye!', style: big)),  ],)<code_end>When a Row’s child is wrapped in an Expanded widget,the Row won’t let this child define its own width anymore.Instead, it defines the Expanded width according to theother children, and only then the Expanded widget forcesthe original child to have the Expanded’s width.In other words, once you use Expanded,the original child’s width becomes irrelevant, and is ignored.<topic_end><topic_start>Example 26<code_start>Row(  children: [    Expanded(      child: Container(        color: red,        child: const Text(          'This is a very long text that won\'t fit the line.',          style: big,        ),      ),    ),    Expanded(      child: Container(        color: green,        child: const Text(          'Goodbye!',          style: big,        ),      ),    ),  ],)<code_end>If all of Row’s children are wrapped in Expanded widgets,each Expanded has a size proportional to its flex parameter,and only then each Expanded widget forces its child to havethe Expanded’s width.In other words, Expanded ignores the preferred width ofits children.<topic_end><topic_start>Example 27<code_start>Row(  children: [    Flexible(      child: Container(        color: red,        child: const Text(          'This is a very long text that won\'t fit the line.',          style: big,        ),      ),    ),    Flexible(      child: Container(        color: green,        child: const Text(          'Goodbye!',          style: big,        ),      ),    ),  ],)<code_end>The only difference if you use Flexible instead of Expanded,is that Flexible lets its child have the same or smallerwidth than the Flexible itself, while Expanded forcesits child to have the exact same width of the Expanded.But both Expanded and Flexible ignore their children’s widthwhen sizing themselves.info Note  This means that it’s impossible to expand Row children  proportionally to their sizes. The Row either uses  the exact child’s width, or ignores it completely  when you use Expanded or Flexible.<topic_end><topic_start>Example 28<code_start>Scaffold(  body: Container(    color: blue,    child: const Column(      children: [        Text('Hello!'),        Text('Goodbye!'),      ],    ),  ),)<code_end>The screen forces the Scaffold to be exactly the same sizeas the screen, so the Scaffold fills the screen.The Scaffold tells the Container that it can be any size it wants,but not bigger than the screen.info Note  When a widget tells its child that it can be smaller than a  certain size, we say the widget supplies loose constraints  to its child. More on that later.<topic_end><topic_start>Example 29<code_start>Scaffold(  body: SizedBox.expand(    child: Container(      color: blue,      child: const Column(        children: [          Text('Hello!'),          Text('Goodbye!'),        ],      ),    ),  ),)<code_end>If you want the Scaffold’s child to be exactly the same sizeas the Scaffold itself, you can wrap its child withSizedBox.expand.<topic_end><topic_start>Tight vs loose constraintsIt’s very common to hear that some constraint is“tight” or “loose”, so what does that mean?<topic_end><topic_start>Tight constraintsA tight constraint offers a single possibility,an exact size. In other words, a tight constrainthas its maximum width equal to its minimum width;and has its maximum height equal to its minimum height.An example of this is the App widget,which is contained by the RenderView class:the box used by the child returned by theapplication’s build function is given a constraintthat forces it to exactly fill the application’s content area(typically, the entire screen).Another example: if you nest a bunch of boxes insideeach other at the root of your application’s render tree,they’ll all exactly fit in each other,forced by the box’s tight constraints.If you go to Flutter’s box.dart file and search forthe BoxConstraints constructors,you’ll find the following:If you revisit Example 2,the screen forces the red Container to beexactly the same size as the screen.The screen achieves that, of course, by passing tightconstraints to the Container.<topic_end><topic_start>Loose constraintsA loose constraint is one that has a minimumof zero and a maximum non-zero.Some boxes loosen the incoming constraints,meaning the maximum is maintained but theminimum is removed, so the widget can havea minimum width and height both equal to zero.Ultimately, Center’s purpose is to transformthe tight constraints it received from its parent(the screen) to loose constraints for its child(the Container).If you revisit Example 3,the Center allows the red Container to be smaller,but not bigger than the screen.<topic_end><topic_start>Unbounded constraintsinfo Note  You might be directed here if the framework  detects a problem involving box constraints.  The Flex section below might also apply.In certain situations,a box’s constraint is unbounded, or infinite.This means that either the maximum width orthe maximum height is set to double.infinity.A box that tries to be as big as possible won’tfunction usefully when given an unbounded constraint and,in debug mode, throws an exception.The most common case where a render box ends upwith an unbounded constraint is within a flex box(Row or Column),and within a scrollable region(such as ListView and other ScrollView subclasses).ListView, for example,tries to expand to fit the space availablein its cross-direction(perhaps it’s a vertically-scrolling block andtries to be as wide as its parent).If you nest a vertically scrolling ListViewinside a horizontally scrolling ListView,the inner list tries to be as wide as possible,which is infinitely wide,since the outer one is scrollable in that direction.The next section describes the error you mightencounter with unbounded constraints in a Flex widget.<topic_end><topic_start>FlexA flex box (Row and Column) behavesdifferently depending on whether itsconstraint is bounded or unbounded inits primary direction.A flex box with a bounded constraint in itsprimary direction tries to be as big as possible.A flex box with an unbounded constraintin its primary direction tries to fit its childrenin that space. Each child’s flex value must beset to zero, meaning that you can’t useExpanded when the flex box is insideanother flex box or a scrollable;otherwise it throws an exception.The cross direction(width for Column or height for Row),must never be unbounded,or it can’t reasonably align its children.<topic_end><topic_start>Learning the layout rules for specific widgetsKnowing the general layout rule is necessary, but it’s not enough.Each widget has a lot of freedom when applying the general rule,so there is no way of knowing how it behaves by just readingthe widget’s name.If you try to guess, you’ll probably guess wrong.You can’t know exactly how a widget behaves unlessyou’ve read its documentation, or studied its source-code.The layout source-code is usually complex,so it’s probably better to just read the documentation.However, if you decide to study the layout source-code,you can easily find it by using the navigating capabilitiesof your IDE.Here’s an example:Find a Column in your code and navigate to itssource code. To do this, use command+B (macOS)or control+B (Windows/Linux) in Android Studio or IntelliJ.You’ll be taken to the basic.dart file.Since Column extends Flex, navigate to the Flexsource code (also in basic.dart).Scroll down until you find a method calledcreateRenderObject(). As you can see,this method returns a RenderFlex.This is the render-object for the Column.Now navigate to the source-code of RenderFlex,which takes you to the flex.dart file.Scroll down until you find a method calledperformLayout(). This is the method that doesthe layout for the Column.Original article by Marcelo GlasbergMarcelo originally published this content asFlutter: The Advanced Layout Rule Even Beginners Must Knowon Medium. We loved it and asked that he allow us to publishin on docs.flutter.dev, to which he graciously agreed. Thanks, Marcelo!You can find Marcelo on GitHub and pub.dev.Also, thanks to Simon Lightfoot for creating theheader image at the top of the article.info Note  To better understand how Flutter implements layout  constraints, check out the following 5-minute video:Decoding Flutter: Unbounded height and width<topic_end><topic_start>Hot reloadFlutter’s hot reload feature helps you quickly andeasily experiment, build UIs, add features, and fix bugs.Hot reload works by injecting updated source code filesinto the running Dart Virtual Machine (VM).After the VM updates classes with the new versions of fields and functions,the Flutter framework automatically rebuilds the widget tree,allowing you to quickly view the effects of your changes.<topic_end><topic_start>How to perform a hot reloadTo hot reload a Flutter app:If you’re working in an IDE/editor that supports Flutter’s IDE tools,select Save All (cmd-s/ctrl-s),or click the hot reload button on the toolbar.If you’re running the app at the command line using flutter run,enter r in the terminal window.After a successful hot reload operation,you’ll see a message in the console similar to:The app updates to reflect your change,and the current state of the app is preserved.Your app continues to execute from where it was priorto run the hot reload command.The code updates and execution continues.What is the difference between hot reload, hot restart,  and full restart?Flutter web currently supports hot restart but not  hot reload.Controls for run, run debug, hot reload, and hot restart in Android StudioA code change has a visible effect only if the modifiedDart code is run again after the change. Specifically,a hot reload causes all the existing widgets to rebuild.Only code involved in the rebuilding of the widgetsis automatically re-executed. The main() and initState()functions, for example, are not run again.<topic_end><topic_start>Special casesThe next sections describe specific scenarios that involvehot reload. In some cases, small changes to the Dart codeenable you to continue using hot reload for your app.In other cases, a hot restart, or a full restart is needed.<topic_end><topic_start>An app is killedHot reload can break when the app is killed.For example, if the app was in the background for too long.<topic_end><topic_start>Compilation errorsWhen a code change introduces a compilation error,hot reload generates an error message similar to:In this situation, simply correct the errors on thespecified lines of Dart code to keep using hot reload.<topic_end><topic_start>CupertinoTabView’s builderHot reload won’t apply changes made toa builder of a CupertinoTabView.For more information, see Issue 43574.<topic_end><topic_start>Enumerated typesHot reload doesn’t work when enumerated types arechanged to regular classes or regular classes arechanged to enumerated types.For example:Before the change:<code_start>enum Color {  red,  green,  blue,}<code_end>After the change:<code_start>class Color {  Color(this.i, this.j);  final int i;  final int j;}<code_end><topic_end><topic_start>Generic typesHot reload won’t work when generic type declarationsare modified. For example, the following won’t work:Before the change:<code_start>class A<T> {  T? i;}<code_end>After the change:<code_start>class A<T, V> {  T? i;  V? v;}<code_end><topic_end><topic_start>Native codeIf you’ve changed native code (such as Kotlin, Java, Swift,or Objective-C), you must perform a full restart (stop andrestart the app) to see the changes take effect.<topic_end><topic_start>Previous state is combined with new codeFlutter’s stateful hot reload preserves the state of your app.This approach enables you to view the effect of the mostrecent change only, without throwing away the current state.For example, if your app requires a user to log in,you can modify and hot reload a page several levels down inthe navigation hierarchy, without re-entering your login credentials.State is kept, which is usually the desired behavior.If code changes affect the state of your app (or its dependencies),the data your app has to work with might not be fully consistentwith the data it would have if it executed from scratch.The result might be different behavior after a hot reloadversus a hot restart.<topic_end><topic_start>Recent code change is included but app state is excludedIn Dart, static fields are lazily initialized.This means that the first time you run a Flutter app and astatic field is read, it’s set to whatever value itsinitializer was evaluated to.Global variables and static fields are treated as state,and are therefore not reinitialized during hot reload.If you change initializers of global variables and static fields,a hot restart or restart the state where the initializers are holdis necessary to see the changes.For example, consider the following code:<code_start>final sampleTable = [  Table(    children: const [      TableRow(        children: [Text('T1')],      )    ],  ),  Table(    children: const [      TableRow(        children: [Text('T2')],      )    ],  ),  Table(    children: const [      TableRow(        children: [Text('T3')],      )    ],  ),  Table(    children: const [      TableRow(        children: [Text('T4')],      )    ],  ),];<code_end>After running the app, you make the following change:<code_start>final sampleTable = [  Table(    children: const [      TableRow(        children: [Text('T1')],      )    ],  ),  Table(    children: const [      TableRow(        children: [Text('T2')],      )    ],  ),  Table(    children: const [      TableRow(        children: [Text('T3')],      )    ],  ),  Table(    children: const [      TableRow(        children: [Text('T10')], // modified      )    ],  ),];<code_end>You hot reload, but the change is not reflected.Conversely, in the following example:<code_start>const foo = 1;final bar = foo;void onClick() {  print(foo);  print(bar);}<code_end>Running the app for the first time prints 1 and 1.Then, you make the following change:<code_start>const foo = 2; // modifiedfinal bar = foo;void onClick() {  print(foo);  print(bar);}<code_end>While changes to const field values are always hot reloaded,the static field initializer is not rerun. Conceptually,const fields are treated like aliases instead of state.The Dart VM detects initializer changes and flags when a setof changes needs a hot restart to take effect.The flagging mechanism is triggered formost of the initialization work in the above example,but not for cases like the following:<code_start>final bar = foo;<code_end>To update foo and view the change after hot reload,consider redefining the field as const or using a getter toreturn the value, rather than using final.For example, either of the following solutions work:<code_start>const foo = 1;const bar = foo; // Convert foo to a const...void onClick() {  print(foo);  print(bar);}<code_end><code_start>const foo = 1;int get bar => foo; // ...or provide a getter.void onClick() {  print(foo);  print(bar);}<code_end>For more information, read about the differencesbetween the const and final keywords in Dart.<topic_end><topic_start>Recent UI change is excludedEven when a hot reload operation appears successful and generates noexceptions, some code changes might not be visible in the refreshed UI.This behavior is common after changes to the app’s main() orinitState() methods.As a general rule, if the modified code is downstream of the rootwidget’s build() method, then hot reload behaves as expected.However, if the modified code won’t be re-executed as a resultof rebuilding the widget tree, then you won’tsee its effects after hot reload.For example, consider the following code:<code_start>import 'package:flutter/material.dart';void main() {  runApp(MyApp());}class MyApp extends StatelessWidget {  const MyApp({super.key});  @override  Widget build(BuildContext context) {    return GestureDetector(onTap: () => print('tapped'));  }}<code_end>After running this app, change the code as follows:<code_start>import 'package:flutter/widgets.dart';void main() {  runApp(const Center(child: Text('Hello', textDirection: TextDirection.ltr)));}<code_end>With a hot restart, the program starts from the beginning,executes the new version of main(),and builds a widget tree that displays the text Hello.However, if you hot reload the app after this change,main() and initState() are not re-executed,and the widget tree is rebuilt with the unchanged instanceof MyApp as the root widget.This results in no visible change after hot reload.<topic_end><topic_start>How it worksWhen hot reload is invoked, the host machine looksat the edited code since the last compilation.The following libraries are recompiled:The source code from those libraries is compiled intokernel files and sent to the mobile device’s Dart VM.The Dart VM re-loads all libraries from the new kernel file.So far no code is re-executed.The hot reload mechanism then causes the Flutter frameworkto trigger a rebuild/re-layout/repaint of all existingwidgets and render objects.<topic_end><topic_start>FAQ<topic_end><topic_start>IntroductionThis page collects some common questions asked aboutFlutter. You might also check out the followingspecialized FAQs:<topic_end><topic_start>What is Flutter?Flutter is Google’s portable UI toolkit for crafting beautiful,natively compiled applications for mobile, web,and desktop from a single codebase.Flutter works with existing code,is used by developers and organizations aroundthe world, and is free and open source.<topic_end><topic_start>Who is Flutter for?For users, Flutter makes beautiful apps come to life.For developers, Flutter lowers the bar to entry for building apps.It speeds app development and reduces the cost and complexityof app production across platforms.For designers, Flutter provides a canvas forhigh-end user experiences. Fast Company describedFlutter as one of the top design ideas of the decade forits ability to turn concepts into production codewithout the compromises imposed by typical frameworks.It also acts as a productive prototyping toolwith drag-and-drop tools like FlutterFlowand web-based IDEs like Zapp!.For engineering managers and businesses,Flutter allows the unification of appdevelopers into a single mobile, web,and desktop app team, building brandedapps for multiple platforms out of a single codebase.Flutter speeds feature development and synchronizesrelease schedules across the entire customer base.<topic_end><topic_start>How much development experience do I need to use Flutter?Flutter is approachable to programmers familiar withobject-oriented concepts (classes, methods, variables,etc) and imperative programming concepts (loops,conditionals, etc).We have seen people with very little programmingexperience learn and use Flutter for prototypingand app development.<topic_end><topic_start>What kinds of apps can I build with Flutter?Flutter is designed to support mobile apps that runon both Android and iOS, as well as interactive appsthat you want to run on your web pages or on the desktop.Apps that need to deliver highly branded designsare particularly well suited for Flutter.However, you can also create pixel-perfect experiencesthat match the Android and iOS design languages with Flutter.Flutter’s package ecosystem supports a widevariety of hardware (such as camera, GPS, network,and storage) and services (such as payments, cloudstorage, authentication, and ads).<topic_end><topic_start>Who makes Flutter?Flutter is an open source project,with contributions from Google and othercompanies and individuals.<topic_end><topic_start>Who uses Flutter?Developers inside and outside of Google useFlutter to build beautiful natively-compiledapps for iOS and Android. To learn about someof these apps, visit the showcase.<topic_end><topic_start>What makes Flutter unique?Flutter is different than most other optionsfor building mobile apps because it doesn’t relyon web browser technology nor the set of widgetsthat ship with each device. Instead, Flutter usesits own high-performance rendering engine to draw widgets.In addition, Flutter is different because it onlyhas a thin layer of C/C++ code. Flutter implementsmost of its system (compositing, gestures, animation,framework, widgets, etc) in Dart (a modern,concise, object-oriented language) that developerscan easily approach read, change, replace, or remove.This gives developers tremendous control over the system,as well as significantly lowers the bar to approachabilityfor the majority of the system.<topic_end><topic_start>Should I build my next production app with Flutter?Flutter 1 was launched on Dec 4th, 2018,Flutter 2 on March 3rd, 2021, andFlutter 3 on May 10th, 2023.As of May 2023, over one million apps have shipped usingFlutter to many hundreds of millions of devices.Check out some sample apps in the showcase.Flutter ships updates on a roughly-quarterlycadence that improve stability and performanceand address commonly-requested user features.<topic_end><topic_start>What does Flutter provide?<topic_end><topic_start>What is inside the Flutter SDK?Flutter includes:<topic_end><topic_start>Does Flutter work with any editors or IDEs?We provide plugins for VS Code,Android Studio, and IntelliJ IDEA.See editor configuration for setup details,and VS Code and Android Studio/IntelliJfor tips on how to use the plugins.Alternatively, you can use the flutter commandfrom a terminal, along with oneof the many editors that support editing Dart.<topic_end><topic_start>Does Flutter come with a framework?Yes! Flutter ships with a modern react-style framework.Flutter’s framework is designed to be layered andcustomizable (and optional). Developers can choose touse only parts of the framework, or even replaceupper layers of the framework entirely.<topic_end><topic_start>Does Flutter come with widgets?Yes! Flutter ships with a set ofhigh-quality Material Design and Cupertino(iOS-style) widgets, layouts, and themes.Of course, these widgets are only a starting point.Flutter is designed to make it easy to create your ownwidgets, or customize the existing widgets.<topic_end><topic_start>Does Flutter support Material Design?Yes! The Flutter and Material teams collaborate closely,and Material is fully supported. For more information,check out the Material 2 and Material 3 widgetsin the widget catalog.<topic_end><topic_start>Does Flutter come with a testing framework?Yes, Flutter provides APIs for writing unit andintegration tests. Learn more about testing with Flutter.We use our own testing capabilities to test our SDK,and we measure our test coverage on every commit.<topic_end><topic_start>Does Flutter come with debugging tools?Yes, Flutter comes with Flutter DevTools (alsocalled Dart DevTools). For more information, seeDebugging with Flutter and the Flutter DevTools docs.<topic_end><topic_start>Does Flutter come with a dependency injection framework?We don’t ship with an opinionated solution,but there are a variety of packages that offerdependency injection and service location,such as injectable, get_it, kiwi, and riverpod.<topic_end><topic_start>Technology<topic_end><topic_start>What technology is Flutter built with?Flutter is built with C, C++, Dart,Skia (a 2D rendering engine),and Impeller (the default rendering engine on iOS).See this architecture diagramfor a better picture of the main components.For a more detailed description of the layered architectureof Flutter, read the architectural overview.<topic_end><topic_start>How does Flutter run my code on Android?The engine’s C and C++ code are compiled with Android’s NDK.The Dart code (both the SDK’s and yours)are ahead-of-time (AOT) compiled into native, ARM, and x86libraries. Those libraries are included in a “runner”Android project, and the whole thing is built into an .apk.When launched, the app loads the Flutter library.Any rendering, input, or event handling, and so on,is delegated to the compiled Flutter and app code.This is similar to the way many game engines work.During debug mode, Flutter uses a virtual machine (VM)to run its code in order to enable stateful hot reload,a feature that lets you make changes to your running codewithout recompilation. You’ll see a “debug” banner inthe top right-hand corner of your app when running in this mode,to remind you that performance is not characteristic ofthe finished release app.<topic_end><topic_start>How does Flutter run my code on iOS?The engine’s C and C++ code are compiled with LLVM.The Dart code (both the SDK’s and yours)are ahead-of-time (AOT) compiled into a native, ARM library.That library is included in a “runner” iOS project,and the whole thing is built into an .ipa.When launched, the app loads the Flutter library.Any rendering, input or event handling, and so on,are delegated to the compiled Flutter and app code.This is similar to the way many game engines work.During debug mode, Flutter uses a virtual machine (VM)to run its code in order to enable stateful hot reload,a feature that lets you make changes to yourrunning code without recompilation. You’ll seea “debug” banner in the top right-hand corner ofyour app when running in this mode, to remind you thatperformance is not characteristic of the finished release app.<topic_end><topic_start>Does Flutter use my operating system’s built-in platform widgets?No. Instead, Flutter provides a set of widgets(including Material Design and Cupertino (iOS-styled) widgets),managed and rendered by Flutter’s framework and engine.You can browse a catalog of Flutter’s widgets.We believe that the end result is higher quality apps.If we reused the built-in platform widgets,the quality and performance of Flutter apps would be limitedby the flexibility and quality of those widgets.In Android, for example, there’s a hard-coded setof gestures and fixed rules for disambiguating them.In Flutter, you can write your own gesture recognizerthat is a first class participant in the gesture system.Moreover, two widgets authored by different people cancoordinate to disambiguate gestures.Modern app design trends point towards designers andusers wanting more motion-rich UIs and brand-first designs.In order to achieve that level of customized, beautiful design,Flutter is architectured to drive pixels insteadof the built-in widgets.By using the same renderer, framework, and set of widgets,it’s easier to publish for multiple platforms from the samecodebase, without having to do careful and costly planningto align different feature sets and API characteristics.By using a single language, a single framework,and a single set of libraries for all of your code(regardless if your UI is different for each platform or not),we also aim to help lower app development and maintenance costs.<topic_end><topic_start>What happens when my mobile OS updates and introduces new widgets?The Flutter team watches the adoption and demand for new mobilewidgets from iOS and Android, and aims to work with the communityto build support for new widgets. This work might come in the formof lower-level framework features, new composable widgets,or new widget implementations.Flutter’s layered architecture is designed to support numerouswidget libraries, and we encourage and support the community inbuilding and maintaining widget libraries.<topic_end><topic_start>What happens when my mobile OS updates and introduces new platform capabilities?Flutter’s interop and plugin system is designed to allowdevelopers to access new mobile OS features and capabilitiesimmediately. Developers don’t have to wait for the Flutter teamto expose the new mobile OS capability.<topic_end><topic_start>What operating systems can I use to build a Flutter app?Flutter supports development using Linux, macOS, ChromeOS,and Windows.<topic_end><topic_start>What language is Flutter written in?Dart, a fast-growing modern language optimizedfor client apps. The underlying graphics frameworkand the Dart virtual machine are implemented in C/C++.<topic_end><topic_start>Why did Flutter choose to use Dart?During the initial development phase,the Flutter team looked at a lot oflanguages and runtimes, and ultimatelyadopted Dart for the framework and widgets.Flutter used four primary dimensions for evaluation,and considered the needs of framework authors,developers, and end users. We found many languagesmet some requirements, but Dart scored highly onall of our evaluation dimensions and met all ourrequirements and criteria.Dart runtimes and compilers support the combination oftwo critical features for Flutter: a JIT-based fastdevelopment cycle that allows for shape changing andstateful hot reloads in a language with types,plus an Ahead-of-Time compiler that emits efficientARM code for fast startup and predictable performance ofproduction deployments.In addition, we have the opportunity to work closelywith the Dart community, which is actively investingresources in improving Dart for use in Flutter. Forexample, when we adopted Dart,the language didn’t have an ahead-of-timetoolchain for producing native binaries,which is instrumental in achieving predictable,high performance, but now the language does because the Dart teambuilt it for Flutter. Similarly, the Dart VM haspreviously been optimized for throughput but theteam is now optimizing the VM for latency, which is moreimportant for Flutter’s workload.Dart scores highly for us on the following primary criteria:<topic_end><topic_start>Can Flutter run any Dart code?Flutter can run Dart code that doesn’t directly ortransitively import dart:mirrors or dart:html.<topic_end><topic_start>How big is the Flutter engine?In March 2021, we measured the download size of aminimal Flutter app (no Material Components,just a single Center widget, built with flutter buildapk --split-per-abi), bundled and compressed as a release APK,to be approximately 4.3 MB for ARM32, and 4.8 MB for ARM64.On ARM32, the core engine is approximately 3.4 MB(compressed), the framework + app code is approximately765 KB (compressed), the LICENSE file is 58 KB(compressed), and necessary Java code (classes.dex)is 120 KB (compressed).In ARM64, the core engine is approximately 4.0 MB (compressed), the framework + app code is approximately659 KB (compressed), the LICENSE file is 58 KB(compressed), and necessary Java code (classes.dex)is 120 KB (compressed).These numbers were measured using apkanalyzer,which is also built into Android Studio.On iOS, a release IPA of the same app has a downloadsize of 10.9 MB on an iPhone X, as reported by Apple’sApp Store Connect. The IPA is larger than the APK mainlybecause Apple encrypts binaries within the IPA, making thecompression less efficient (see theiOS App Store Specific Considerationssection of Apple’s QA1795).info Note  The release engine binary used to include LLVM IR (bitcode).  However, Apple deprecated bitcode in Xcode 14 and removed support,  so it has been removed from the Flutter 3.7 release.Of course, we recommend that you measure your own app.To do that, see Measuring your app’s size.<topic_end><topic_start>How does Flutter define a pixel?Flutter uses logical pixels,and often refers to them merely as “pixels”.Flutter’s devicePixelRatio expresses the ratiobetween physical pixels and logical CSS pixels.<topic_end><topic_start>Capabilities<topic_end><topic_start>What kind of app performance can I expect?You can expect excellent performance. Flutter isdesigned to help developers easily achieve a constant 60fps.Flutter apps run using natively compiled code—nointerpreters are involved.This means that Flutter apps start quickly.<topic_end><topic_start>What kind of developer cycles can I expect? How long between edit and refresh?Flutter implements a hot reload developer cycle. You can expectsub-second reload times, on a device or an emulator/simulator.Flutter’s hot reload is stateful so the app stateis retained after a reload. This means you can quickly iterateon a screen deeply nested in your app, without startingfrom the home screen after every reload.<topic_end><topic_start>How is hot reload different from hot restart?Hot reload works by injecting updated source code filesinto the running Dart VM (Virtual Machine). This doesn’tonly add new classes, but also adds methods and fieldsto existing classes, and changes existing functions.Hot restart resets the state to the app’s initial state.For more information, see Hot reload.<topic_end><topic_start>Where can I deploy my Flutter app?You can compile and deploy your Flutter app to iOS, Android,web, and desktop.<topic_end><topic_start>What devices and OS versions does Flutter run on?We support and test running Flutter on a varietyof low-end to high-end platforms.  For a detailed listof the platforms on which we test, see the list of supported platforms.Flutter supports building ahead-of-time (AOT) compiled librariesfor x86_64, armeabi-v7a, and arm64-v8a.Apps built for ARMv7 or ARM64 run fine (using ARM emulation)on many x86 Android devices.We support developing Flutter apps on a range of platforms.See the system requirements listed under eachdevelopment operating system.<topic_end><topic_start>Does Flutter run on the web?Yes, web support is available in the stable channel.For more details, check out the web instructions.<topic_end><topic_start>Can I use Flutter to build desktop apps?Yes, desktop support is in stable for Windows,macOS, and Linux.<topic_end><topic_start>Can I use Flutter inside of my existing native app?Yes, learn more in the add-to-app section of our website.<topic_end><topic_start>Can I access platform services and APIs like sensors and local storage?Yes. Flutter gives developers out-of-the-box access to someplatform-specific services and APIs from the operating system.However, we want to avoid the “lowest common denominator” problemwith most cross-platform APIs, so we don’t intend to buildcross-platform APIs for all native services and APIs.A number of platform services and APIs haveready-made packages available on pub.dev.Using an existing package is easy.Finally, we encourage developers to use Flutter’sasynchronous message passing system to create yourown integrations with platform and third-party APIs.Developers can expose as much or as little of theplatform APIs as they need, and build layers ofabstractions that are a best fit for their project.<topic_end><topic_start>Can I extend and customize the bundled widgets?Absolutely. Flutter’s widget system was designedto be easily customizable.Rather than having each widget provide a large number of parameters,Flutter embraces composition. Widgets are built out of smallerwidgets that you can reuse and combine in novel ways to makecustom widgets. For example, rather than subclassing a genericbutton widget, ElevatedButton combines a Material widget with aGestureDetector widget. The Material widget provides the visualdesign and the GestureDetector widget provides theinteraction design.To create a button with a custom visual design, you can combinewidgets that implement your visual design with a GestureDetector,which provides the interaction design. For example,CupertinoButton follows this approach and combines aGestureDetector with several other widgets that implement itsvisual design.Composition gives you maximum control over the visual andinteraction design of your widgets while also allowing alarge amount of code reuse. In the framework, we’ve decomposedcomplex widgets to pieces that separately implementthe visual, interaction, and motion design. You can remixthese widgets however you like to make your own customwidgets that have full range of expression.<topic_end><topic_start>Why would I want to share layout code across iOS and Android?You can choose to implement different app layouts foriOS and Android. Developers are free to check the mobile OSat runtime and render different layouts,though we find this practice to be rare.More and more, we see mobile app layouts and designs evolvingto be more brand-driven and unified across platforms.This implies a strong motivation to share layout and UIcode across iOS and Android.The brand identity and customization of the app’saesthetic design is now becoming more important thanstrictly adhering to traditional platform aesthetics.For example, app designs often require custom fonts, colors,shapes, motion, and more in order to clearly convey theirbrand identity.We also see common layout patterns deployed acrossiOS and Android. For example, the “bottom nav bar”pattern can now be naturally found across iOS and Android.There seems to be a convergence of design ideasacross mobile platforms.<topic_end><topic_start>Can I interop with my mobile platform’s default programming language?Yes, Flutter supports calling into the platform,including integrating with Java or Kotlin code on Android,and ObjectiveC or Swift code on iOS.This is enabled by a flexible message passing stylewhere a Flutter app might send and receive messagesto the mobile platform using a BasicMessageChannel.Learn more about accessing platform and third-party servicesin Flutter with platform channels.Here is an example project that shows how to use aplatform channel to access battery state information oniOS and Android.<topic_end><topic_start>Does Flutter come with a reflection / mirrors system?No. Dart includes dart:mirrors,which provides type reflection. But sinceFlutter apps are pre-compiled for production,and binary size is always a concern with mobile apps,this library is unavailable for Flutter apps.Using static analysis we can strip out anything that isn’tused (“tree shaking”). If you import a huge Dart librarybut only use a self-contained two-line method,then you only pay the cost of the two-line method,even if that Dart library itself imports dozens anddozens of other libraries. This guarantee is only secureif Dart can identify the code path at compile time.To date, we’ve found other approaches for specific needsthat offer a better trade-off, such as code generation.<topic_end><topic_start>How do I do international­ization (i18n), localization (l10n), and accessibility (a11y) in Flutter?Learn more about i18n and l10n in theinternationalization tutorial.Learn more about a11y in theaccessibility documentation.<topic_end><topic_start>How do I write parallel and/or concurrent apps for Flutter?Flutter supports isolates. Isolates are separate heaps inFlutter’s VM, and they are able to run in parallel(usually implemented as separate threads). Isolatescommunicate by sending and receiving asynchronous messages.Check out an example of using isolates with Flutter.<topic_end><topic_start>Can I run Dart code in the background of a Flutter app?Yes, you can run Dart code in a background process on bothiOS and Android. For more information, see the free Medium articleExecuting Dart in the Background with Flutter Plugins and Geofencing.<topic_end><topic_start>Can I use JSON/XML/protobuffers (and so on) with Flutter?Absolutely. There are libraries onpub.dev for JSON, XML, protobufs,and many other utilities and formats.For a detailed writeup on using JSON with Flutter,check out the JSON tutorial.<topic_end><topic_start>Can I build 3D (OpenGL) apps with Flutter?Today we don’t support 3D using OpenGL ES or similar.We have long-term plans to expose an optimized 3D API,but right now we’re focused on 2D.<topic_end><topic_start>Why is my APK or IPA so big?Usually, assets including images, sound files, fonts, and so on,are the bulk of an APK or IPA. Various tools in theAndroid and iOS ecosystems can help you understandwhat’s inside of your APK or IPA.Also, be sure to create a release buildof your APK or IPA with the Flutter tools.A release build is usually much smallerthan a debug build.Learn more about creating arelease build of your Android app,and creating a release build of your iOS app.Also, check out Measuring your app’s size.<topic_end><topic_start>Do Flutter apps run on Chromebooks?We have seen Flutter apps run on some Chromebooks.We are tracking issues related to running Flutter onChromebooks.<topic_end><topic_start>Is Flutter ABI compatible?Flutter and Dart don’t offer application binary interface (ABI)compatibility. Offering ABI compatibility is not a currentgoal for Flutter or Dart.<topic_end><topic_start>Framework<topic_end><topic_start>Why is the build() method on State, rather than StatefulWidget?Putting a Widget build(BuildContext context) method on Staterather putting a Widget build(BuildContext context, State state)method on StatefulWidget gives developers more flexibility whensubclassing StatefulWidget. You can read a moredetailed discussion on the API docs for State.build.<topic_end><topic_start>Where is Flutter’s markup language? Why doesn’t Flutter have a markup syntax?Flutter UIs are built with an imperative, object-orientedlanguage (Dart, the same language used to build Flutter’sframework). Flutter doesn’t ship with a declarative markup.We found that UIs dynamically built with code allow formore flexibility. For example, we have found it difficultfor a rigid markup system to express and producecustomized widgets with bespoke behaviors.We have also found that our “code-first” approach better allowsfor features like hot reload and dynamic environment adaptations.It’s possible to create a custom language that is thenconverted to widgets on the fly. Because build methodsare “just code”, they can do anything,including interpreting markup and turning it into widgets.<topic_end><topic_start>My app has a Debug banner/ribbon in the upper right. Why am I seeing that?By default, the flutter run command uses thedebug build configuration.The debug configuration runs your Dart code in a VM (Virtual Machine)enabling a fast development cycle with hot reload(release builds are compiled using the standard Androidand iOS toolchains).The debug configuration also checks all asserts, which helpsyou catch errors early during development, but imposes aruntime cost. The “Debug” banner indicates that these checksare enabled. You can run your app without these checks byusing either the --profile or --release flag to flutter run.If your IDE uses the Flutter plugin,you can launch the app in profile or release mode.For VS Code, use the Run > Start debuggingor Run > Run without debugging menu entries.For IntelliJ, use the menu entriesRun > Flutter Run in Profile Mode or Release Mode.<topic_end><topic_start>What programming paradigm does Flutter’s framework use?Flutter is a multi-paradigm programming environment.Many programming techniques developed over the past few decadesare used in Flutter. We use each one where we believethe strengths of the technique make it particularly well-suited.In no particular order:<topic_end><topic_start>Project<topic_end><topic_start>Where can I get support?If you think you’ve encountered a bug, file it in ourissue tracker. You might also useStack Overflow for “HOWTO” type questions.For discussions, join our mailing list atflutter-dev@googlegroups.com or seek us out on Discord.For more information, see our Community page.<topic_end><topic_start>How do I get involved?Flutter is open source, and we encourage you to contribute.You can start by simply filing issues for feature requestsand bugs in our issue tracker.We recommend that you join our mailing list atflutter-dev@googlegroups.com and let us know how you’reusing Flutter and what you’d like to do with it.If you’re interested in contributing code, you can startby reading our Contributing guide, and check out ourlist of easy starter issues.Finally, you can connect with helpful Flutter communities.For more information, see the Community page.<topic_end><topic_start>Is Flutter open source?Yes, Flutter is open source technology.You can find the project on GitHub.<topic_end><topic_start>Which software license(s) apply to Flutter and its dependencies?Flutter includes two components: an engine that ships as adynamically linked binary, and the Dart framework as a separatebinary that the engine loads. The engine uses multiple softwarecomponents with many dependencies; view the complete listin its license file.The framework is entirely self-contained and requiresonly one license.In addition, any Dart packages you use might have theirown license requirements.<topic_end><topic_start>How can I determine the licenses my Flutter application needs to show?There’s an API to find the list of licenses you need to show:If your application has a Drawer, add anAboutListTile.If your application doesn’t have a Drawer but does use theMaterial Components library, call either showAboutDialogor showLicensePage.For a more custom approach, you can get the raw licenses from theLicenseRegistry.<topic_end><topic_start>Who works on Flutter?We all do! Flutter is an open source project.Currently, the bulk of the development is doneby engineers at Google. If you’re excited about Flutter,we encourage you to join the community andcontribute to Flutter!<topic_end><topic_start>What are Flutter’s guiding principles?We believe the following:We are focused on three things:<topic_end><topic_start>Will Apple reject my Flutter app?We can’t speak for Apple, but their App Store containsmany apps built with framework technologies such as Flutter.Indeed, Flutter uses the same fundamental architecturalmodel as Unity, the engine that powers many of themost popular games on the Apple store.Apple has frequently featured well-designed appsthat are built with Flutter,including Hamilton and Reflectly.As with any app submitted to the Apple store,apps built with Flutter should follow Apple’sguidelines for App Store submission.<topic_end><topic_start>Books about FlutterHere’s a collection of books about Flutter,in alphabetical order.If you find another one that we should add,file an issue and (feel free to)submit a PR (sample) to add it yourself.Also, check the Flutter version that the bookwas written under. Anything published beforeFlutter 3.10/Dart 3 (May 2023),won’t reflect the latest version of Dart andmight not include null safety;anything published before Flutter 3.16 (November 2023)won’t reflect that Material 3 is nowFlutter’s default theme.See the what’s newpage to view Flutter’s latest release.  The following sections have more information about each book.<topic_end><topic_start>Beginning App Development with Flutterby Rap PayneEasy to understand starter book. Currently the best-selling and highest rated Flutter book on Amazon.<topic_end><topic_start>Beginning Flutter: A Hands On Guide to App Developmentby Marco L. NapoliBuild your first app in Flutter - no experience necessary.<topic_end><topic_start>Flutter Apps Development: Build Cross-Platform Flutter Apps with Trustby Mouaz M. Al-ShahmehThis book teaches what you need to know to build your first Flutter app. You will learn about the basics of Flutter (widgets, state management, and navigation), as well as how to build a variety of different app types (games, social media apps, and e-commerce apps). By the end of this book, you will be able to build beautiful, high-performance mobile apps using Flutter.<topic_end><topic_start>Flutter Apprenticeby Michael Katz, Kevin David Moore, Vincent Ngo, and Vincenzo GuzziBuild for both iOS and Android With Flutter! With Flutter and Flutter Apprentice, you can achieve the dream of building fast applications, faster.<topic_end><topic_start>Flutter Complete Reference 2.0by Alberto MiolaThe book’s first eight chapters are dedicated to Dart 3.0 and all its features. Over 500 pages are about the Flutter framework: widgets basics, state management, animations, navigation, and more.<topic_end><topic_start>Flutter: Développez vos applications mobiles multiplateformes avec Dartby Julien TrillardCe livre sur Flutter s’adresse aux développeurs, initiés comme plus aguerris, qui souhaitent disposer des connaissances nécessaires pour créer de A à Z des applications mobiles multiplateformes avec le framework de Google<topic_end><topic_start>Flutter for Beginnersby Thomas Bailey and Alessandro BiessekAn introductory guide to building cross-platform mobile applications with Flutter 2.5 and Dart.<topic_end><topic_start>Flutter in Actionby Eric WindmillFlutter in Action teaches you to build professional-quality mobile applications using the Flutter SDK and the Dart programming language.<topic_end><topic_start>Flutter Libraries We Loveby Codemagic“Flutter libraries We Love” focuses on 11 different categories of Flutter libraries. Each category lists available libraries as well as a highlighted library that is covered in more detail – including pros and cons, developer’s perspective, and real-life code examples. The code snippets of all 11 highlighted libraries support Flutter 2 with sound null safety.<topic_end><topic_start>Flutter Succinctlyby Ed FreitasApp UI in Flutter-from Zero to Hero.<topic_end><topic_start>Google Flutter Mobile Development Quick Start Guideby Prajyot Mainkar and Salvatore GirodanoA fast-paced guide to get you started with cross-platform mobile application development with Google Flutter<topic_end><topic_start>Learn Google Flutter Fastby Mark ClowLearn Google Flutter by example. Over 65 example mini-apps.<topic_end><topic_start>Managing State in Flutter Pragmaticallyby Waleed ArshadExplore popular state management techniques in Flutter.<topic_end><topic_start>Practical Flutterby Frank ZammettiImprove your Mobile Development with Google’s latest open source SDK.<topic_end><topic_start>Pragmatic Flutterby Priyanka TyagiBuilding Cross-Platform Mobile Apps for Android, iOS, Web & Desktop<topic_end><topic_start>Programming Flutterby Carmine ZaccagninoNative, Cross-Platform Apps the Easy Way. Doesn’t require previous Dart knowledge.<topic_end><topic_start>Flutter Architecturesby Atul VashishtWrite code with a good architecture<topic_end><topic_start>VideosThese Flutter videos, produced both internallyat Google and by the Flutter community,might help if you are a visual learner.Note that many people make Flutter videos.This page shows some that we like,but there are many others, includingsome in different languages.<topic_end><topic_start>Series<topic_end><topic_start>Flutter ForwardEven if you couldn’t make it to Nairobi for the in-personevent, you can enjoy the content created for Flutter Forward!Flutter Forward livestreamFlutter Forward playlist<topic_end><topic_start>Get ready for Flutter ForwardFlutter Forward is happening on Jan 25th, in Nairobi, Kenya.Before the event, the Flutter team provided 17 days of Flutterfeaturing new content and activities leading up to the event.This playlist contains these and other pre-event videos relating toFlutter Forward.17 Days of Flutter!Get ready for Flutter Forward playlist<topic_end><topic_start>Learning to Flyinfo Note  Season 2 of Learning to Fly has been released as part  of the 17 Days of Flutter, leading up to the 3.7  release! Season 2 centers around creating a platform  game, DoodleDash, using the Flame engine.Follow along with Khanh’s journey as she learns Flutter.From ideation down to the moments of confusion,learn alongside her as she asks important questions like:“What is the best way to build out a theme? How to approach using fonts?”And more!Building my first Flutter app | Learning to FlyLearning to Fly playlistHere are some of the series offered on theflutterdev YouTube channel.For more information, check out allof the Flutter channel’s playlists.<topic_end><topic_start>Decoding FlutterThis series focuses on tips, tricks, best practices,and general advice on getting the most out of Flutter.In the comments section for this series,you can submit suggestions future episodes.Introducing Decoding FlutterDecoding Flutter playlist<topic_end><topic_start>Flutter widget of the weekDo you have 60 seconds?Each one-minute video highlights a Flutter widget.Introducing widget of the weekFlutter widget of the week playlist<topic_end><topic_start>Flutter package of the weekIf you have another minute (or two),each of these videos highlights a Flutter package.flutter_slidable packageFlutter package of the week playlist<topic_end><topic_start>The Boring Flutter showThis series features Flutter programmers coding,unscripted and in real time. Mistakes, solutions(some of them correct), and snazzy intro music included.Introducing the Boring Flutter showThe Boring Flutter show playlist<topic_end><topic_start>Flutter at Google I/O 2022Google I/O 2022 is over, but you can still catchthe great Flutter content!Flutter at Google I/O playlist<topic_end><topic_start>Flutter Engage 2021Flutter Engage 2021 was a day-long event thatofficially launched Flutter 2.Check out the Flutter Engage 2021 highlights reel.Watch recordings of the sessions on the Flutter YouTube channel.KeynoteFlutter Engage 2021 playlistWatch recordings of the sessions offered by the Flutter community.Flutter Engage community talks playlist<topic_end><topic_start>Flutter in focusFive-to-ten minute tutorials (more or less) on using Flutter.Introducing Flutter in focusFlutter in focus playlist<topic_end><topic_start>Conference talksHere are a some Flutter talks given at various conferences.Conference talks playlist<topic_end><topic_start>Flutter developer storiesVideos showing how various customers, such as Abbey Road Studio, Hamilton,and Alibaba, have used Flutter to create beautiful compelling apps withmillions of downloads.Flutter developer stories playlist<topic_end><topic_start>Online coursesLearn how to build Flutter apps with these video courses.Before signing up for a course, verify that it includesup-to-date information, such as null-safe Dart code.These courses are listed alphabetically.To include your course, submit a PR:<topic_end><topic_start>Bootstrap into DartNew to the Dart language?We compiled our favorite resources tohelp you quickly learn Dart.Many people have reported thatDart is easy and fun to learn.We hope these resources make Dart easy foryou to learn, too.Want to learn more and perhaps contribute?Check out the Dart community.<topic_end><topic_start>Create useful bug reportsThe instructions in this document detail the current stepsrequired to provide the most actionable bug reports forcrashes and other bad behavior. Each step is optional butwill greatly improve how quickly issues are diagnosed and addressed.We appreciate your effort in sending us as much feedback as possible.<topic_end><topic_start>Create an issue on GitHub<topic_end><topic_start>Provide a minimal reproducible code sampleCreate a minimal Flutter app that shows the problem you are facing,and paste it into the GitHub issue.To create it you can use flutter create bug command and updatethe main.dart file.Alternatively, you can use DartPad, which is capableof creating and running small Flutter apps.If your problem goes out of what can be placed in a single file, for exampleyou have a problem with native channels, you can upload the full code ofthe reproduction into a separate repository and link it.<topic_end><topic_start>Provide some Flutter diagnostics<topic_end><topic_start>Run the command in verbose modeFollow these steps only if your issue is related to theflutter tool.<topic_end><topic_start>Provide the most recent logs<topic_end><topic_start>Provide the crash report<topic_end><topic_start>Issues: flutter/flutterHave a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.  By clicking “Sign up for GitHub”, you agree to our terms of service and  privacy statement. We’ll occasionally send you account related emails.    Already on GitHub?    Sign in    to your account  <topic_end><topic_start>Issues list<topic_end><topic_start>Who is Dash?This is Dash:Dash is the mascot for the Dart language and the Flutter framework.info Note  You can now make your own digital Dash using  Dashatar, a Flutter app for web and mobile!<topic_end><topic_start>How did it all start?As soon as Shams Zakhour started working as aDart writer at Google in December 2013,she started advocating for a Dart mascot.After documenting Java for 14 years, shehad observed how beloved the Java mascot,Duke, had become,and she wanted something similar for Dart.But the idea didn’t gain momentum until 2017,when one of the Flutter engineers, Nina Chen,suggested it on an internal mailing list.The Flutter VP at the time, Joshy Joseph,approved the idea and asked theorganizer for the 2018 Dart Conference,Linda Rasmussen, to make it happen.Once Shams heard about these plans,she rushed to Linda and asked to own and drivethe project to produce the plushies for the conference.Linda had already elicited some design sketches,which she handed off.Starting with the sketches, Shams located a vendorwho could work within an aggressive deadline(competing with Lunar New Year),and started the process of creatingthe specs for the plushy.That’s right, Dash was originally aDart mascot, not a Flutter mascot.Here are some early mockups and one of the first prototypes:  The first prototype had uneven eyes<topic_end><topic_start>Why a hummingbird?Early on, a hummingbird image was created for the Dart teamto use for presentations and the web.The hummingbird represents that Dart is a speedy language.However, hummingbirds are pointed and angularand we wanted a cuddly plushy, so we chose a roundhummingbird.Shams specified which color would go where,the tail shape, the tuft of hair, the eyes…all thelittle details. The vendor sent the specs to twomanufacturers who returned the prototypes some weeks later. Introducing Dash at the January 2018 Dart Conference.While the manufacturing process was proceeding,Shams chose a name for the plushy: Dash,because it was an early code name for theDart project, it was gender neutral,and it seemed appropriate for a hummingbird.Many boxes of Dash plushies arrived insouthern California just in time for the conference.They were eagerly adopted by Dart and Flutter enthusiasts.The people have spoken, so Dash is now the mascot for Flutter and Dart.Dash 1.0Conference swagSince the creation of Dash 1.0, we’ve made two more versions.Marketing slightly changed the Dart and Flutter color scheme afterDash 1.0 was created, so Dash 2.0 reflects the updated scheme(which removed the green color).Dash 2.1 is a smaller size and has a few more colortweaks. The smaller size is easier to ship,and fits better in a claw machine!Dash 2.0 and 2.1<topic_end><topic_start>Dash factsWe also have Mega-Dash, a life-sized mascotwho is currently resting in a Google office.Mega-Dash made herfirst appearance at the Flutter Interact eventin Brooklyn, New York, on December 11, 2019.We also have a Dash puppet that Shams made fromone of the first plushies.A number of our YouTube videos feature the Dash puppet,voiced by Emily Fortuna, one of the early (and much loved)Flutter Developer Advocates.<topic_end><topic_start>Flutter widget indexThis is an alphabetical list of nearly every widget that is bundled withFlutter. You can also browse widgets by category.You might also want to check out our Widget of the Week video serieson the Flutter YouTube channel. Each shortepisode features a different Flutter widget. For more video series, seeour videos page.Widget of the Week playlistA widget that absorbs pointers during hit testing. When absorbing is true, this widget prevents its subtree from receiving pointer events by terminating hit testing...Hovering containers that prompt app users to provide more data or make a decision.A widget that aligns its child within itself and optionally sizes itself based on the child's size.Animated transition that moves the child's position over a given duration whenever the given alignment changes.A general-purpose widget for building animations. AnimatedBuilder is useful for more complex widgets that wish to include animation as part of a larger build function....A container that gradually changes its values over a period of time.A widget that cross-fades between two given children and animates itself between their sizes.Animated version of DefaultTextStyle which automatically transitions the default text style (the text style to apply to descendant Text widgets without explicit style) over a...A scrolling container that animates items when they are inserted or removed.The state for a scrolling container that animates items when they are inserted or removed.A widget that prevents the user from interacting with widgets behind itself.Animated version of Opacity which automatically transitions the child's opacity over a given duration whenever the given opacity changes.Animated version of PhysicalModel.Animated version of Positioned which automatically transitions the child's position over a given duration whenever the given position changes.Animated widget that automatically transitions its size over a given duration whenever the given child's size changes.A widget that rebuilds when the given Listenable changes value.Container that displays content and actions at the top of a screen.A widget that attempts to size the child to a specific aspect ratio.Asset bundles contain resources, such as images and strings, that can be used by an application. Access to these resources is asynchronous so that they...A widget for helping the user make a selection by entering some text and choosing from among a list of options.A widget that applies a filter to the existing painted content and then paints a child. This effect is relatively expensive, especially if the filter...Icon-like block that conveys dynamic content such as counts or status. It can include labels or numbers.Container that positions its child according to the child's baseline.Container that displays navigation and key actions at the bottom of a screen.Containers that anchor supplementary content to the bottom of the screen.Container that includes tools to explore and switch between top-level views in a single tap.Bottom sheets slide up from the bottom of the screen to reveal more content. You can call showBottomSheet() to implement a persistent bottom sheet or...Container for short, related pieces of content displayed in a box with rounded corners and a drop shadow.Alignment block that centers its child within itself.Form control that app users can set or clear to select one or more options from a set.Small blocks that simplify entering information, making selections, filtering content, or triggering actions.Circular progress indicator that spins to indicate a busy application.A widget that clips its child using an oval.A widget that clips its child using a path.A widget that clips its child using a rectangle.Layout a list of child widgets in the vertical direction.Clickable blocks that start an action, such as sending an email, sharing a document, or liking a comment.A widget that imposes additional constraints on its child.A convenience widget that combines common painting, positioning, and sizing widgets.An iOS-style modal bottom action sheet to choose an option among many.An iOS-style activity indicator. Displays a circular 'spinner'.An iOS-style alert dialog.An iOS-style button.An iOS-style full-screen modal route that opens when the child is long-pressed. Used to display relevant actions for your content.An iOS-style date or date and time picker.A button typically used in a CupertinoAlertDialog.An iOS-style transition used for summoning fullscreen dialogs.Container that uses the iOS style to display a scrollable view.A block that uses the iOS style to create a row in a list.Container at the top of a screen that uses the iOS style. Many developers use this with `CupertinoPageScaffold`.Basic iOS style page layout structure. Positions a navigation bar and content on a background.Provides an iOS-style page transition animation.An iOS-style picker control. Used to select an item in a short list.Rounded rectangle surface that looks like an iOS popup surface, such as an alert dialog or action sheet.An iOS-style scrollbar that indicates which portion of a scrollable widget is currently visible.An iOS-style search field.An iOS-style segmented control. Used to select mutually exclusive options in a horizontal list.Used to select from a range of values.An iOS-13-style segmented control. Used to select mutually exclusive options in a horizontal list.An iOS-styled navigation bar with iOS-11-style large titles using slivers.An iOS-style switch. Used to toggle the on/off state of a single setting.An iOS-style bottom tab bar. Typically used with CupertinoTabScaffold.Tabbed iOS app structure. Positions a tab bar on top of tabs of content.Root content of a tab that supports parallel navigation between tabs. Typically used with CupertinoTabScaffold.An iOS-style text field.An iOS-style countdown timer picker.A widget that uses a delegate to size and position multiple children.A widget that provides a canvas on which to draw during the paint phase.A ScrollView that creates custom scroll effects using slivers.A widget that defers the layout of its single child to a delegate.Data tables display sets of raw data. They usually appear in desktop enterprise products. The DataTable widget implements this component.Calendar interface used to select a date or a range of dates.A widget that paints a Decoration either before or after its child paints.Animated version of a DecoratedBox that animates the different properties of its Decoration.The text style to apply to descendant Text widgets without explicit style.A widget that can be dismissed by dragging in the indicated direction. Dragging or flinging this widget in the DismissDirection causes the child to slide...Thin line that groups content in lists and containers.A widget that receives data when a Draggable widget is dropped. When a draggable is dragged on top of a drag target, the drag target...A widget that can be dragged from to a DragTarget. When a draggable widget recognizes the start of a drag gesture, it displays a feedback...A container for a Scrollable that responds to drag gestures by resizing the scrollable until a limit is reached, and then scrolling.A Material Design panel that slides in horizontally from the edge of a Scaffold to show navigation links in an application.Shows the currently selected item and an arrow that opens a menu for selecting another item.A Material Design elevated button. A filled button whose material elevates when pressed.A widget that drops all the semantics of its descendants. This can be used to hide subwidgets that would otherwise be reported but that would...A widget that expands a child of a Row, Column, or Flex.Expansion panels contain creation flows and allow lightweight editing of an element. The ExpansionPanel widget implements this component.Clickable block that triggers an action. These wider blocks can fit a text label and provide a larger target area.Animates the opacity of a widget.Scales and positions its child within itself according to fit.Clickable block containing an icon that keeps a key action always in reach.A widget that implements the flow layout algorithm.The Flutter logo, in widget form. This widget respects the IconTheme.An optional container for grouping together multiple form field widgets (e.g. TextField widgets).A single form field. This widget maintains the current state of the form field, so that updates and validation errors are visually reflected in the...A widget that applies a translation expressed as a fraction of the box's size before painting its child.A widget that sizes its child to a fraction of the total available space. For more details about the layout algorithm, see RenderFractionallySizedOverflowBox.Widget that builds itself based on the latest snapshot of interaction with a Future.A widget that detects gestures. Attempts to recognize gestures that correspond to its non-null callbacks. If this widget has a child, it defers to that...A grid list consists of a repeated pattern of cells arrayed in a vertical and horizontal layout. The GridView widget implements this component.A widget that marks its child as being a candidate for hero animations.A Material Design icon.Clickable icons to prompt app users to take supplementary actions.A widget that is invisible during hit testing. When ignoring is true, this widget (and its subtree) is invisible to hit testing. It still consumes...A widget that displays an image.An abstract class for building widgets that animate changes to their properties.A Stack that shows a single child from a list of children.A widget that enables pan and zoom interactions with its child.A widget that sizes its child to the child's intrinsic height.A widget that sizes its child to the child's intrinsic width.A widget that calls a callback whenever the user presses or releases a key on a keyboard.Builds a widget tree that can depend on the parent widget's size.A box that limits its size only when it's unconstrained.Vertical line that changes color as an ongoing process, such as loading an app or submitting a form, completes.A widget that arranges its children sequentially along a given axis, forcing them to the dimension of the parent in the other axis.A single fixed-height row that typically contains some text as well as a leading or trailing icon.A scrollable, linear list of widgets. ListView is the most commonly used scrolling widget. It displays its children one after another in the scroll direction....Makes its child draggable starting from long press.A convenience widget that wraps a number of widgets that are commonly required for applications implementing Material Design.Establishes a subtree in which media queries resolve to the given data.Container that displays a list of choices on a temporary surface.A widget that merges the semantics of its descendants.Persistent container on the leading edge of tablet and desktop screens to navigate to parts of an app.Persistent container that enables switching between primary destinations in an app.Container that slides from the leading edge of the app to navigate to other sections in an app.A widget that manages a set of child widgets with a stack discipline. Many apps have a navigator near the top of their widget hierarchy...A scrolling view inside of which can be nested other scrolling views, with their scroll positions being intrinsically linked.A widget that listens for Notifications bubbling up the tree.A widget that lays the child out as if it was in the tree, but without painting anything, without making the child available for hit...A widget that makes its child partially transparent.A Material Design outlined button, essentially a TextButton with an outlined border.A widget that imposes different constraints on its child than it gets from its parent, possibly allowing the child to overflow the parent.A widget that insets its child by the given padding.A scrollable list that works page by page.A widget that draws a box that represents where other widgets will one day be added.Displays a menu when pressed and calls onSelected when the menu is dismissed because an item was selected.Animated version of Positioned which takes a specific Animation to transition the child's position from a start position to and end position over the lifetime...Form control that app users can set or clear to select only one option from a set.A widget that displays a dart:ui.Image directly.A Material Design pull-to-refresh wrapper for scrollables.A list whose items the user can interactively reorder by dragging.The RichText widget displays text that uses multiple different styles. The text to display is described using a tree of TextSpan objects, each of which...A widget that rotates its child by a integral number of quarter turns.Animates the rotation of a widget.Layout a list of child widgets in the horizontal direction.Implements the basic Material Design visual layout structure. This class provides APIs for showing drawers, snack bars, and bottom sheets.Animates the scale of transformed widget.Controls how Scrollable widgets behave in a subtree.Scrollable implements the interaction model for a scrollable widget, including gesture recognition, but does not have an opinion about how the viewport, which actually displays...A Material Design scrollbar. A scrollbar indicates which portion of a Scrollable widget is actually visible.Single or multiple selected clickable blocks to help people select options, switch views, or sort elements.A widget that annotates the widget tree with a description of the meaning of the widgets. Used by accessibility tools, search engines, and other semantic...Simple dialogs can provide additional details or actions about a list item. For example they can display avatars icons clarifying subtext or orthogonal actions (such...A box in which a single widget can be scrolled. This widget is useful when you have a single box that will normally be entirely...Animates its own size and clips and aligns the child.A box with a specified size. If given a child, this widget forces its child to have a specific width and/or height (assuming values are...A widget that is a specific size but passes its original constraints through to its child, which will probably overflow.Animates the position of a widget relative to its normal position.Form control that enables selecting a range of values.A material design app bar that integrates with a CustomScrollView.A delegate that supplies children for slivers using a builder callback.A delegate that supplies children for slivers using an explicit list.A sliver that places multiple box children with the same main axis extent in a linear array.A sliver that places multiple box children in a two dimensional arrangement.A sliver that places multiple box children in a linear array along the main axis.A sliver that applies padding on each side of another sliver.A sliver whose size varies when the sliver is scrolled to the edge of the viewport opposite the sliver's GrowthDirection.A sliver that contains a single box widget.Brief messages about app processes that display at the bottom of the screen.This class is useful if you want to overlap several children in a simple way, for example having some text and an image, overlaid with...A Material Design stepper widget that displays progress through a sequence of steps.Widget that builds itself based on the latest snapshot of interaction with a Stream.Toggle control that changes the state of a single item to on or off.Layered containers that organize content across different screens, data sets, and other interactions.A page view that displays the widget which corresponds to the currently selected tab. Typically used in conjunction with a TabBar.Coordinates tab selection between a TabBar and a TabBarView.Displays a row of small circular indicators, one per tab. The selected tab's indicator is highlighted. Often used in conjunction with a TabBarView.Displays child widgets in rows and columns.A run of text with a single style.A Material Design text button. A simple flat button without a border outline.Box into which app users can enter text. They appear in forms and dialogs.Applies a theme to descendant widgets. A theme describes the colors and typographic choices of an application.Clock interface used to select and set a specific time.Tooltips provide text labels that help explain the function of a button or other user interface action. Wrap the button in a Tooltip widget to...A widget that applies a transformation before painting its child.A convenience class that wraps a number of widgets that are commonly required for an application.A widget that displays its children in multiple horizontal or vertical runs.<topic_end><topic_start>flutter: The Flutter command-line toolThe flutter command-line tool is how developers (or IDEs on behalf ofdevelopers) interact with Flutter. For Dart related commands,you can use the dart command-line tool.Here’s how you might use the flutter tool to create, analyze, test, and run anapp:To run pub commands using the flutter tool:To view all commands that flutter supports:To get the current version of the Flutter SDK, including its framework, engine,and tools:<topic_end><topic_start>flutter commandsThe following table shows which commands you can use with the flutter tool:For additional help on any of the commands, enter flutter help <command>or follow the links in the More information column.You can also get details on pub commands — for example,flutter help pub outdated.<topic_end><topic_start>: This is an alphabetical list of nearly every widget that is bundled with Flutter.AbsorbPointer: A widget that absorbs pointers during hit testing. When absorbing is true, this widget prevents its subtree from receiving pointer events by terminating hit testing at itself. It still consumes space during layout and paints its child as usual. It just prevents its children from being the target of located events, because it returns true from RenderBox.hitTest.AlertDialog: Hovering containers that prompt app users to provide more data or make a decision.Align: A widget that aligns its child within itself and optionally sizes itself based on the child's size.AnimatedAlign: Animated transition that moves the child's position over a given duration whenever the given alignment changes.AnimatedBuilder: A general-purpose widget for building animations. AnimatedBuilder is useful for more complex widgets that wish to include animation as part of a larger build function To use AnimatedBuilder, construct the widget and pass it a builder function.AnimatedContainer: A container that gradually changes its values over a period of time.AnimatedCrossFade: A widget that cross-fades between two given children and animates itself between their sizes.AnimatedDefaultTextStyle: Animated version of DefaultTextStyle which automatically transitions the default text style (the text style to apply to descendant Text widgets without explicit style) over a given duration whenever the given style changes.AnimatedList: A scrolling container that animates items when they are inserted or removed.AnimatedListState: The state for a scrolling container that animates items when they are inserted or removed.AnimatedModalBarrier: A widget that prevents the user from interacting with widgets behind itself.AnimatedOpacity: Animated version of Opacity which automatically transitions the child's opacity over a given duration whenever the given opacity changes.AnimatedPhysicalModel: Animated version of PhysicalModel.AnimatedPositioned: Animated version of Positioned which automatically transitions the child's position over a given duration whenever the given position changes.AnimatedSize: Animated widget that automatically transitions its size over a given duration whenever the given child's size changes.AnimatedWidget: A widget that rebuilds when the given Listenable changes value.AppBar: Container that displays content and actions at the top of a screen.AspectRatio: A widget that attempts to size the child to a specific aspect ratio.AssetBundle: Asset bundles contain resources, such as images and strings, that can be used by an application. Access to these resources is asynchronous so that they can be transparently loaded over a network (e.g., from a NetworkAssetBundle) or from the local file system without blocking the application's user interface.Applications have a rootBundle, which contains the resources that were packaged with the application when it was built.Autocomplete: A widget for helping the user make a selection by entering some text and choosing from among a list of options.BackdropFilter: A widget that applies a filter to the existing painted content and then paints a child. The filter will be applied to all the area within its parent or ancestor widget's clip. If there's no clip, the filter will be applied to the full screen.The results of the filter will be blended back into the background using the blendMode parameter. The only value for blendMode that is supported on all platforms is BlendMode.srcOver which works well for most scenes.Badge: Icon-like block that conveys dynamic content such as counts or status. It can include labels or numbers.Baseline: Container that positions its child according to the child's baseline.Bottom app bar: Container that displays navigation and key actions at the bottom of a screen.Bottom sheet: Containers that anchor supplementary content to the bottom of the screen.BottomNavigationBar: Container that includes tools to explore and switch between top-level views in a single tap.BottomSheet: Bottom sheets slide up from the bottom of the screen to reveal more content. You can call showBottomSheet() to implement a persistent bottom sheet or A modal bottom sheet The BottomSheet widget itself is rarely used directly. Instead, prefer to create a persistent bottom sheet with ScaffoldState.showBottomSheet or Scaffold.bottomSheet, and a modal bottom sheet with showModalBottomSheet.Card: Container for short, related pieces of content displayed in a box with rounded corners and a drop shadow.Center: Alignment block that centers its child within itself.Checkbox: Form control that app users can set or clear to select one or more options from a set.Chip: Small blocks that simplify entering information, making selections, filtering content, or triggering actions.CircularProgressIndicator: Circular progress indicator that spins to indicate a busy application.ClipOval: A widget that clips its child using an oval.ClipPath: A widget that clips its child using a path.ClipRect: A widget that clips its child using a rectangle.Column: Layout a list of child widgets in the vertical direction.Common buttons: Clickable blocks that start an action, such as sending an email, sharing a document, or liking a comment.ConstrainedBox: A widget that imposes additional constraints on its child.Container: A convenience widget that combines common painting, positioning, and sizing widgets.CupertinoActionSheet: An iOS-style modal bottom action sheet to choose an option among many.CupertinoActivityIndicator: An iOS-style activity indicator. Displays a circular 'spinner'.CupertinoAlertDialog: An iOS-style alert dialog.CupertinoButton: An iOS-style button.CupertinoContextMenu: An iOS-style full-screen modal route that opens when the child is long-pressed. Used to display relevant actions for your content.CupertinoDatePicker: An iOS-style date or date and time picker.CupertinoDialogAction: A button typically used in a CupertinoAlertDialog.CupertinoFullscreenDialogTransition: An iOS-style transition used for summoning fullscreen dialogs.CupertinoListSection: Container that uses the iOS style to display a scrollable view.CupertinoListTile: A block that uses the iOS style to create a row in a list.CupertinoNavigationBar: Container at the top of a screen that uses the iOS style. Many developers use this with `CupertinoPageScaffold`.CupertinoPageScaffold: Basic iOS style page layout structure. Positions a navigation bar and content on a background.CupertinoPageTransition: Provides an iOS-style page transition animation.CupertinoPicker: An iOS-style picker control. Used to select an item in a short list.CupertinoPopupSurface: Rounded rectangle surface that looks like an iOS popup surface, such as an alert dialog or action sheet.CupertinoScrollbar: An iOS-style scrollbar that indicates which portion of a scrollable widget is currently visible.CupertinoSearchTextField: An iOS-style search field.CupertinoSegmentedControl: An iOS-style segmented control. Used to select mutually exclusive options in a horizontal list.CupertinoSlider: Used to select from a range of values.CupertinoSlidingSegmentedControl: An iOS-13-style segmented control. Used to select mutually exclusive options in a horizontal list.CupertinoSliverNavigationBar: An iOS-styled navigation bar with iOS-11-style large titles using slivers.CupertinoSwitch: An iOS-style switch. Used to toggle the on/off state of a single setting.CupertinoTabBar: An iOS-style bottom tab bar. Typically used with CupertinoTabScaffold.CupertinoTabScaffold: Tabbed iOS app structure. Positions a tab bar on top of tabs of content.CupertinoTabView: Root content of a tab that supports parallel navigation between tabs. Typically used with CupertinoTabScaffold.CupertinoTextField: An iOS-style text field.CupertinoTimerPicker: An iOS-style countdown timer picker.CustomMultiChildLayout: A widget that uses a delegate to size and position multiple children.CustomPaint: A widget that provides a canvas on which to draw during the paint phase.CustomScrollView: A ScrollView that creates custom scroll effects using slivers.CustomSingleChildLayout: A widget that defers the layout of its single child to a delegate.DataTable: Data tables display sets of raw data. They usually appear in desktop enterprise products. The DataTable widget implements this component.DatePicker: Calendar interface used to select a date or a range of dates.DecoratedBox: A widget that paints a Decoration either before or after its child paints.DecoratedBoxTransition: Animated version of a DecoratedBox that animates the different properties of its Decoration.DefaultTextStyle: The text style to apply to descendant Text widgets without explicit style.Dismissible: A widget that can be dismissed by dragging in the indicated direction. Dragging or flinging this widget in the DismissDirection causes the child to slide  out of view. Following the slide animation, if resizeDuration is non-null, the Dismissible widget animates its height (or width, whichever is perpendicular to the dismiss direction) to zero over the resizeDuration.Divider: Thin line that groups content in lists and containers.DragTarget: A widget that receives data when a Draggable widget is dropped. When a draggable is dragged on top of a drag target, the drag target  is asked whether it will accept the data the draggable is carrying. If the user does drop the draggable on top of the drag target (and the drag target has indicated that it will accept the draggable's data), then the drag target is asked to accept the draggable's data.Draggable: A widget that can be dragged from to a DragTarget. When a draggable widget recognizes the start of a drag gesture, it displays a feedback widget that tracks the user's finger across the screen. If the user lifts their finger while on top of a DragTarget, that target is given the opportunity to accept the data carried by the draggable.The ignoringFeedbackPointer defaults to true, which means that the feedback widget ignores the pointer during hit testing.Similarly, ignoringFeedbackSemantics defaults to true, and the feedback also ignores semantics when building the semantics tree.On multitouch devices, multiple drags can occur simultaneously because there can be multiple pointers in contact with the device at once.: To limit the number of simultaneous drags, use the maxSimultaneousDrags property. The default is to allow an unlimited number of simultaneous drags.This widget displays child when zero drags are under way. If childWhenDragging is non-null, this widget instead displays childWhenDragging when one or more drags are underway. Otherwise, this widget always displays child.DraggableScrollableSheet: A container for a Scrollable that responds to drag gestures by resizing the scrollable until a limit is reached, and then scrolling.Drawer: A Material Design panel that slides in horizontally from the edge of a Scaffold to show navigation links in an application.DropdownButton: Shows the currently selected item and an arrow that opens a menu for selecting another item.ElevatedButton: A Material Design elevated button. A filled button whose material elevates when pressed.ExcludeSemantics: A widget that drops all the semantics of its descendants. This can be used to hide subwidgets that would otherwise be reported but that would  only be confusingExpanded: A widget that expands a child of a Row, Column, or Flex.ExpansionPanel: Expansion panels contain creation flows and allow lightweight editing of an element. The ExpansionPanel widget implements this component.Extended FloatingActionButton: Clickable block that triggers an action. These wider blocks can fit a text label and provide a larger target area.FadeTransition: Animates the opacity of a widget.FittedBox: Scales and positions its child within itself according to fit.FloatingActionButton: Clickable block containing an icon that keeps a key action always in reach.Flow: A widget that implements the flow layout algorithm.FlutterLogo: The Flutter logo, in widget form. This widget respects the IconTheme.Form: An optional container for grouping together multiple form field widgets (e.g. TextField widgets).FormField: A single form field. This widget maintains the current state of the form field, so that updates and validation errors are visually reflected in the UIFractionalTranslation: A widget that applies a translation expressed as a fraction of the box's size before painting its child.FractionallySizedBox: A widget that sizes its child to a fraction of the total available space. For more details about the layout algorithm, see RenderFractionallySizedOverflowBox.FutureBuilder: Widget that builds itself based on the latest snapshot of interaction with a Future.GestureDetector: A widget that detects gestures. Attempts to recognize gestures that correspond to its non-null callbacks. If this widget has a child, it defers to that that child for its sizing behavior. If it does not have a child, it grows to fit the parent instead.GridView: A grid list consists of a repeated pattern of cells arrayed in a vertical and horizontal layout. The GridView widget implements this component.Hero: A widget that marks its child as being a candidate for hero animations.Icon: A Material Design icon.IconButton: Clickable icons to prompt app users to take supplementary actions.IgnorePointer: A widget that is invisible during hit testing. When ignoring is true, this widget (and its subtree) is invisible to hit testing. It still consumes space during layout and paints its child as usual. It just cannot be the target of located events, because it returns false from RenderBox.hitTest.Image: A widget that displays an image.ImplicitlyAnimatedWidget: An abstract class for building widgets that animate changes to their properties.IndexedStack: A Stack that shows a single child from a list of children.InteractiveViewer: A widget that enables pan and zoom interactions with its child.IntrinsicHeight: A widget that sizes its child to the child's intrinsic height.IntrinsicWidth: A widget that sizes its child to the child's intrinsic width.KeyboardListener: A widget that calls a callback whenever the user presses or releases a key on a keyboard.LayoutBuilder: Builds a widget tree that can depend on the parent widget's size.LimitedBox: A box that limits its size only when it's unconstrained.LinearProgressIndicator: Vertical line that changes color as an ongoing process, such as loading an app or submitting a form, completes.ListBody: A widget that arranges its children sequentially along a given axis, forcing them to the dimension of the parent in the other axis.ListTile: A single fixed-height row that typically contains some text as well as a leading or trailing icon.ListView: A scrollable, linear list of widgets. ListView is the most commonly used scrolling widget. It displays its children one after another in the scroll direction In the cross axis, the children are required to fill the ListView.LongPressDraggable: Makes its child draggable starting from long press.MaterialApp: A convenience widget that wraps a number of widgets that are commonly required for applications implementing Material Design.MediaQuery: Establishes a subtree in which media queries resolve to the given data.Menu: Container that displays a list of choices on a temporary surface.MergeSemantics: A widget that merges the semantics of its descendants.Navigation rail: Persistent container on the leading edge of tablet and desktop screens to navigate to parts of an app.NavigationBar: Persistent container that enables switching between primary destinations in an app.NavigationDrawer: Container that slides from the leading edge of the app to navigate to other sections in an app.Navigator: A widget that manages a set of child widgets with a stack discipline. Many apps have a navigator near the top of their widget hierarchy in order to display their logical history using an Overlay with the most recently visited pages visually on top of the older pages. Using this pattern lets the navigator visually transition from one page to another by moving the widgets around in the overlay. Similarly, the navigator can be used to show a dialog by positioning the dialog widget above the current page.NestedScrollView: A scrolling view inside of which can be nested other scrolling views, with their scroll positions being intrinsically linked.NotificationListener: A widget that listens for Notifications bubbling up the tree.Offstage: A widget that lays the child out as if it was in the tree, but without painting anything, without making the child available for hit testing, and without taking any room in the parent.Offstage children are still active: they can receive focus and have keyboard input directed to them.Opacity: A widget that makes its child partially transparent.OutlinedButton: A Material Design outlined button, essentially a TextButton with an outlined border.OverflowBox: A widget that imposes different constraints on its child than it gets from its parent, possibly allowing the child to overflow the parent.Padding: A widget that insets its child by the given padding.PageView: A scrollable list that works page by page.Placeholder: A widget that draws a box that represents where other widgets will one day be added.PopupMenuButton: Displays a menu when pressed and calls onSelected when the menu is dismissed because an item was selected.PositionedTransition: Animated version of Positioned which takes a specific Animation to transition the child's position from a start position to and end position over the lifetimeof the animation.Only works if it's the child of a Stack.Radio: Form control that app users can set or clear to select only one option from a set.RawImage: A widget that displays a dart:ui.Image directly.RefreshIndicator: A Material Design pull-to-refresh wrapper for scrollables.ReorderableListView: A list whose items the user can interactively reorder by dragging.RichText: The RichText widget displays text that uses multiple different styles. The text to display is described using a tree of TextSpan objects, each of which has an associated style that is used for that subtree. The text might break across multiple lines or might all be displayed on the same line depending on the layout constraints.RotatedBox: A widget that rotates its child by a integral number of quarter turns.RotationTransition: Animates the rotation of a widget.Row: Layout a list of child widgets in the horizontal direction.Scaffold: Implements the basic Material Design visual layout structure. This class provides APIs for showing drawers, snack bars, and bottom sheets.ScaleTransition: Animates the scale of transformed widget.ScrollConfiguration: Controls how Scrollable widgets behave in a subtree.Scrollable: Scrollable implements the interaction model for a scrollable widget, including gesture recognition, but does not have an opinion about how the viewport, which actually displays displays the children, is constructed.Scrollbar: A Material Design scrollbar. A scrollbar indicates which portion of a Scrollable widget is actually visible.SegmentedButton: Single or multiple selected clickable blocks to help people select options, switch views, or sort elements.Semantics: A widget that annotates the widget tree with a description of the meaning of the widgets. Used by accessibility tools, search engines, and other semantic analysis software to determine the meaning of the application.SimpleDialog: Simple dialogs can provide additional details or actions about a list item. For example they can display avatars icons clarifying subtext or orthogonal actionsSingleChildScrollView: A box in which a single widget can be scrolled. This widget is useful when you have a single box that will normally be entirely visible, for example a clock face in a time picker, but you need to make sure it can be scrolled if the container gets too small in one axis (the scroll direction).SizeTransition: Animates its own size and clips and aligns the child.SizedBox: A box with a specified size. If given a child, this widget forces its child to have a specific width and/or height .SizedOverflowBox: A widget that is a specific size but passes its original constraints through to its child, which will probably overflow.SlideTransition: Animates the position of a widget relative to its normal position.Slider: Form control that enables selecting a range of values.SliverAppBar: A material design app bar that integrates with a CustomScrollView.SliverChildBuilderDelegate: A delegate that supplies children for slivers using a builder callback.SliverChildListDelegate: A delegate that supplies children for slivers using an explicit list.SliverFixedExtentList: A sliver that places multiple box children with the same main axis extent in a linear array.SliverGrid: A sliver that places multiple box children in a two dimensional arrangement.SliverList: A sliver that places multiple box children in a linear array along the main axis.SliverPadding: A sliver that applies padding on each side of another sliver.SliverPersistentHeader: A sliver whose size varies when the sliver is scrolled to the edge of the viewport opposite the sliver's GrowthDirection.SliverToBoxAdapter: A sliver that contains a single box widget.SnackBar: Brief messages about app processes that display at the bottom of the screen.Stack: This class is useful if you want to overlap several children in a simple way, for example having some text and an image, overlaid with a gradient and a button attached to the bottom.Stepper: A Material Design stepper widget that displays progress through a sequence of steps.StreamBuilder: Widget that builds itself based on the latest snapshot of interaction with a Stream.Switch: Toggle control that changes the state of a single item to on or off.TabBar: Layered containers that organize content across different screens, data sets, and other interactions.TabBarView: A page view that displays the widget which corresponds to the currently selected tab. Typically used in conjunction with a TabBar.TabController: Coordinates tab selection between a TabBar and a TabBarView.TabPageSelector: Displays a row of small circular indicators, one per tab. The selected tab's indicator is highlighted. Often used in conjunction with a TabBarView.Table: Displays child widgets in rows and columns.Text: A run of text with a single style.TextButton: A Material Design text button. A simple flat button without a border outline.TextField: Box into which app users can enter text. They appear in forms and dialogs.Theme: Applies a theme to descendant widgets. A theme describes the colors and typographic choices of an application.TimePicker: Clock interface used to select and set a specific time.Tooltip: Tooltips provide text labels that help explain the function of a button or other user interface action. Wrap the button in a Tooltip widget to provide a message which will be shown when the widget is long pressed.Transform: A widget that applies a transformation before painting its child.WidgetsApp: A convenience class that wraps a number of widgets that are commonly required for an application.Wrap: A widget that displays its children in multiple horizontal or vertical runs.<topic_end>