diff --git a/firebase.json b/firebase.json index 6ba6cbba133..ab24115b7bd 100644 --- a/firebase.json +++ b/firebase.json @@ -63,7 +63,7 @@ { "source": "/f/flutter-survey-metadata.json", "destination": "https://storage.googleapis.com/flutter-uxr/surveys/flutter-survey-metadata.json", "type": 301 }, { "source": "/faq", "destination": "/resources/faq", "type": 301 }, { "source": "/fastlane-cd", "destination": "/deployment/cd#fastlane", "type": 301 }, - { "source": "/flutter-for-:platform*", "destination": "/get-started/flutter-for/:platform*-devs", "type": 301 }, + { "source": "/flutter-for-:platform*", "destination": "/flutter-for/:platform*-devs", "type": 301 }, { "source": "/formatting", "destination": "/tools/formatting", "type": 301 }, { "source": "/gestures", "destination": "/ui/advanced/gestures", "type": 301 }, { "source": "/glossary", "destination": "/resources/glossary", "type": 301 }, @@ -93,8 +93,8 @@ { "source": "/responsive*", "destination": "/ui/layout/responsive/adaptive-responsive", "type": 301 }, { "source": "/routing-and-navigation", "destination": "/ui/navigation", "type": 301 }, { "source": "/sdk-archive", "destination": "/install/archive", "type": 301 }, - { "source": "/setup-:os*", "destination": "/get-started", "type": 301 }, - { "source": "/setup", "destination": "/get-started", "type": 301 }, + { "source": "/setup-:os*", "destination": "/install", "type": 301 }, + { "source": "/setup", "destination": "/install", "type": 301 }, { "source": "/technical-overview", "destination": "/resources/architectural-overview", "type": 301 }, { "source": "/text-input", "destination": "/cookbook/forms/text-input", "type": 301 }, { "source": "/tutorial", "destination": "/learn/tutorial", "type": 301 }, @@ -106,7 +106,7 @@ { "source": "/using-ide-vscode", "destination": "/tools/vs-code", "type": 301 }, { "source": "/using-ide", "destination": "/tools/android-studio", "type": 301 }, { "source": "/using-packages", "destination": "/packages-and-plugins/using-packages", "type": 301 }, - { "source": "/web-analogs", "destination": "/get-started/flutter-for/web-devs", "type": 301 }, + { "source": "/web-analogs", "destination": "/flutter-for/web-devs", "type": 301 }, { "source": "/webflutter.dev/web", "destination": "/web", "type": 301 }, { "source": "/widgets-intro", "destination": "/ui", "type": 301 }, { "source": "/widgets", "destination": "/ui/widgets/catalog", "type": 301 }, @@ -134,22 +134,28 @@ { "source": "/cookbook/testing/widget-test-introduction", "destination": "/cookbook/testing/widget", "type": 301 }, { "source": "/cookbook/testing/widget-test-tap-drag", "destination": "/cookbook/testing/widget/tap-drag", "type": 301 }, { "source": "/deployment/fastlane-cd", "destination": "/deployment/cd#fastlane", "type": 301 }, - { "source": "/get-started/codelab-web", "destination": "/get-started/codelab", "type": 301 }, + { "source": "/get-started", "destination": "/install", "type": 301 }, + { "source": "/get-started/custom", "destination": "/install/custom", "type": 301 }, + { "source": "/get-started/codelab", "destination": "/learn/pathway", "type": 301 }, + { "source": "/get-started/codelab-web", "destination": "/learn", "type": 301 }, { "source": "/get-started/editor", "destination": "/tools/editors", "type": 301 }, - { "source": "/get-started/flutter-for", "destination": "/get-started/learn-flutter", "type": 301 }, - { "source": "/get-started/flutter-for/ios-devs", "destination": "/get-started/flutter-for/swiftui-devs", "type": 301 }, - { "source": "/get-started/fwe/:rest*", "destination": "/get-started/fundamentals/:rest*", "type": 301 }, - { "source": "/get-started/install", "destination": "/get-started", "type": 301 }, + { "source": "/get-started/flutter-for", "destination": "/learn", "type": 301 }, + { "source": "/get-started/flutter-for/ios-devs", "destination": "/flutter-for/swiftui-devs", "type": 301 }, + { "source": "/get-started/flutter-for/:rest*", "destination": "/flutter-for/:rest*", "type": 301 }, + { "source": "/get-started/fwe/*", "destination": "/learn/pathway", "type": 301 }, + { "source": "/get-started/fundamentals", "destination": "/learn/pathway", "type": 301 }, + { "source": "/get-started/fundamentals/*", "destination": "/learn/pathway", "type": 301 }, + { "source": "/get-started/install", "destination": "/install", "type": 301 }, { "source": "/get-started/install/help", "destination": "/install/troubleshoot", "type": 301 }, - { "source": "/get-started/install/:rest*", "destination": "/get-started", "type": 301 }, - { "source": "/get-started/learn-more", "destination": "/get-started/learn-flutter", "type": 301 }, - { "source": "/get-started/test-drive*", "destination": "/get-started/codelab", "type": 301 }, + { "source": "/get-started/learn-flutter", "destination": "/learn", "type": 301 }, + { "source": "/get-started/learn-more", "destination": "/learn", "type": 301 }, + { "source": "/get-started/test-drive*", "destination": "/learn/pathway", "type": 301 }, { "source": "/get-started/uninstall", "destination": "/install/uninstall", "type": 301 }, { "source": "/get-started/web", "destination": "/platform-integration/web/building", "type": 301 }, + { "source": "/get-started/quick", "destination": "/install/quick", "type": 301 }, { "source": "/ios-14", "destination": "/platform-integration/ios/ios-debugging", "type": 301 }, { "source": "/ios-project-migration", "destination": "https://web.archive.org/web/20220614103526/https://docs.flutter.dev/development/ios-project-migration", "type": 301 }, { "source": "/layout", "destination": "/ui/layout", "type": 301 }, - { "source": "/learn", "destination": "/get-started/learn-flutter", "type": 301 }, { "source": "/material-3-migration", "destination": "/release/breaking-changes/material-3-migration", "type": 301 }, { "source": "/packages-and-plugins/androidx-compatibility", "destination": "/platform-integration/android/androidx-migration", "type": 301 }, { "source": "/packages-and-plugins/c-interop", "destination": "/platform-integration/android/c-interop", "type": 301 }, @@ -191,7 +197,7 @@ { "source": "/resources/ai-overview", "destination": "/ai/create-with-ai", "type": 301 }, { "source": "/resources/books", "destination": "https://docs.flutter.dev/reference/learning-resources", "type": 301 }, { "source": "/resources/compatibility", "destination": "/release/compatibility-policy", "type": 301 }, - { "source": "/resources/dart-swift-concurrency", "destination": "/get-started/flutter-for/dart-swift-concurrency", "type": 301 }, + { "source": "/resources/dart-swift-concurrency", "destination": "/flutter-for/dart-swift-concurrency", "type": 301 }, { "source": "/resources/platform-adaptations", "destination": "/platform-integration/platform-adaptations", "type": 301 }, { "source": "/resources/rendering", "destination": "/resources/architectural-overview#rendering-and-layout", "type": 301 }, { "source": "/resources/security-false-positives", "destination": "/reference/security-false-positives", "type": 301 }, @@ -850,7 +856,7 @@ { "source": "/to/switch-flutter-version", "destination": "/install/upgrade#switch-to-a-specific-flutter-version", "type": 301 }, { "source": "/to/team-infra", "destination": "https://github.com/flutter/flutter/blob/main/docs/triage/Infra-Triage.md", "type": 301 }, { "source": "/to/template", "destination": "https://flutter.dev/go/template", "type": 301 }, - { "source": "/to/test-drive", "destination": "/get-started/quick", "type": 301 }, + { "source": "/to/test-drive", "destination": "/install", "type": 301 }, { "source": "/to/track-widget-creation", "destination": "/tools/devtools/inspector#track-widget-creation", "type": 301 }, { "source": "/to/troubleshoot-devices", "destination": "/install/troubleshoot", "type": 301 }, { "source": "/to/unbounded-constraints", "destination": "/ui/layout/constraints#unbounded", "type": 301 }, diff --git a/site/lib/src/style_hash.dart b/site/lib/src/style_hash.dart index ce114383cc9..ccfd83ccbe2 100644 --- a/site/lib/src/style_hash.dart +++ b/site/lib/src/style_hash.dart @@ -2,4 +2,4 @@ // dart format off /// The generated hash of the `main.css` file. -const generatedStylesHash = ''; +const generatedStylesHash = 'kqT7v0wrEV/S'; diff --git a/src/_includes/docs/get-started/setup-next-steps.html b/src/_includes/docs/get-started/setup-next-steps.html index a43d50c1ad1..c7a27ca0fb9 100644 --- a/src/_includes/docs/get-started/setup-next-steps.html +++ b/src/_includes/docs/get-started/setup-next-steps.html @@ -42,7 +42,7 @@ Write your first app
  • - Learn the fundamentals + Learn the fundamentals
  • extends Command { [`Result` class]: /app-architecture/design-patterns/result [pub.dev]: {{site.pub}} [`command_it`]: {{site.pub-pkg}}/command_it -[`ChangeNotifier`]: /get-started/fundamentals/state-management +[`ChangeNotifier`]: /learn/tutorial/change-notifier [Model-View-ViewModel (MVVM)]: /app-architecture/guide#view-models [View models]: /app-architecture/guide#view-models diff --git a/src/content/app-architecture/design-patterns/sql.md b/src/content/app-architecture/design-patterns/sql.md index 6aa4d528304..012d61a3de8 100644 --- a/src/content/app-architecture/design-patterns/sql.md +++ b/src/content/app-architecture/design-patterns/sql.md @@ -471,7 +471,7 @@ TodoListScreen( [Flutter Architecture design]:/app-architecture [Flutter architecture recommendations]:/app-architecture -[MVVM pattern]:/get-started/fundamentals/state-management#using-mvvm-for-your-applications-architecture +[MVVM pattern]:/app-architecture/guide#mvvm [Persist data with SQLite]:/cookbook/persistence/sqlite [Persistent storage architecture: Key-value data]:/app-architecture/design-patterns/key-value-data [`/examples/app-architecture/todo_data_service/`]: {{site.repo.this}}/tree/main/examples/app-architecture/todo_data_service/ diff --git a/src/content/app-architecture/guide.md b/src/content/app-architecture/guide.md index 736b52ad42d..5c220119d44 100644 --- a/src/content/app-architecture/guide.md +++ b/src/content/app-architecture/guide.md @@ -196,7 +196,7 @@ check out the [state management fundamentals][]. [UI layer]: /app-architecture/case-study/ui-layer [App architecture case study]: /app-architecture/case-study -[state management fundamentals]: /get-started/fundamentals/state-management +[state management fundamentals]: /data-and-backend/state-mgmt/intro ## Data layer diff --git a/src/content/get-started/flutter-for/android-devs.md b/src/content/flutter-for/android-devs.md similarity index 100% rename from src/content/get-started/flutter-for/android-devs.md rename to src/content/flutter-for/android-devs.md diff --git a/src/content/get-started/flutter-for/compose-devs.md b/src/content/flutter-for/compose-devs.md similarity index 100% rename from src/content/get-started/flutter-for/compose-devs.md rename to src/content/flutter-for/compose-devs.md diff --git a/src/content/get-started/flutter-for/dart-swift-concurrency.md b/src/content/flutter-for/dart-swift-concurrency.md similarity index 100% rename from src/content/get-started/flutter-for/dart-swift-concurrency.md rename to src/content/flutter-for/dart-swift-concurrency.md diff --git a/src/content/get-started/flutter-for/declarative.md b/src/content/flutter-for/declarative.md similarity index 100% rename from src/content/get-started/flutter-for/declarative.md rename to src/content/flutter-for/declarative.md diff --git a/src/content/get-started/flutter-for/index.md b/src/content/flutter-for/index.md similarity index 100% rename from src/content/get-started/flutter-for/index.md rename to src/content/flutter-for/index.md diff --git a/src/content/get-started/flutter-for/react-native-devs.md b/src/content/flutter-for/react-native-devs.md similarity index 100% rename from src/content/get-started/flutter-for/react-native-devs.md rename to src/content/flutter-for/react-native-devs.md diff --git a/src/content/get-started/flutter-for/swiftui-devs.md b/src/content/flutter-for/swiftui-devs.md similarity index 100% rename from src/content/get-started/flutter-for/swiftui-devs.md rename to src/content/flutter-for/swiftui-devs.md diff --git a/src/content/get-started/flutter-for/uikit-devs.md b/src/content/flutter-for/uikit-devs.md similarity index 100% rename from src/content/get-started/flutter-for/uikit-devs.md rename to src/content/flutter-for/uikit-devs.md diff --git a/src/content/get-started/flutter-for/web-devs.md b/src/content/flutter-for/web-devs.md similarity index 100% rename from src/content/get-started/flutter-for/web-devs.md rename to src/content/flutter-for/web-devs.md diff --git a/src/content/get-started/flutter-for/xamarin-forms-devs.md b/src/content/flutter-for/xamarin-forms-devs.md similarity index 100% rename from src/content/get-started/flutter-for/xamarin-forms-devs.md rename to src/content/flutter-for/xamarin-forms-devs.md diff --git a/src/content/get-started/codelab.md b/src/content/get-started/codelab.md deleted file mode 100644 index 605a8b08399..00000000000 --- a/src/content/get-started/codelab.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: Write your first Flutter app -description: How to write an app in Flutter. -shortTitle: Write your first app -prev: - title: Set up Flutter - path: /get-started -next: - title: Learn more - path: /get-started/learn-flutter -showToc: false ---- - -You are now ready to start the "First Flutter app" codelab. -In about an hour and a half, -you will learn the basics of Flutter -by creating an app -that works on mobile, desktop, and web. - -
    - The illustration of the initial and the resulting app that you will build in this codelab. - - {%- comment %} - The ▶ character below is the "black right-pointing triangle" - Unicode character. - The non-breaking space after it makes the button look nicer. - {% endcomment -%} - - ▶  Start codelab - - -
    - -:::tip -The codelab above walks you through writing your first Flutter -app for all platforms — mobile, desktop, and web. -::: - -If you prefer an instructor-led version of this codelab, -check out the following workshop: - - diff --git a/src/content/get-started/fundamentals/dart.md b/src/content/get-started/fundamentals/dart.md deleted file mode 100644 index 482f0d1dc48..00000000000 --- a/src/content/get-started/fundamentals/dart.md +++ /dev/null @@ -1,183 +0,0 @@ ---- -title: Intro to Dart -description: Learn about the Dart programming language -prev: - title: Fundamentals - path: /get-started/fundamentals -next: - title: Widgets - path: /get-started/fundamentals/widgets ---- - -To get started with Flutter, -you need to have some familiarity with -the Dart programming language, which Flutter -applications are written in. -This page is a gentle introduction to Dart, -and if you're comfortable reading the -code examples, feel free to skip this page. -You do not need to be an expert in Dart to -continue with this series. - -## Dart - -Flutter applications are built in [Dart][], -a language that will look familiar -to anyone who's written Java, Javascript, -or any other C-like language. - -:::note -Installing Flutter also installs Dart, -so you don't need to install Dart separately. -::: - -The following example is a small program that -fetches data from dart.dev, -decodes the returned json, -and prints it to the console. -If you're confident in your ability to -understand this program, -feel free to skip to the next page. - -```dart -import 'dart:convert'; -import 'package:http/http.dart' as http; - -class Package { - final String name; - final String latestVersion; - final String? description; - - Package(this.name, this.latestVersion, {this.description}); - - @override - String toString() { - return 'Package{name: $name, latestVersion: $latestVersion, description: $description}'; - } -} - -void main() async { - final httpPackageUrl = Uri.https('dart.dev', '/f/packages/http.json'); - final httpPackageResponse = await http.get(httpPackageUrl); - if (httpPackageResponse.statusCode != 200) { - print('Failed to retrieve the http package!'); - return; - } - final json = jsonDecode(httpPackageResponse.body); - final package = Package( - json['name'], - json['latestVersion'], - description: json['description'], - ); - print(package); -} -``` - -This program has two parts: -the `Package` class declaration, and the business logic, -which is contained in the [`main`][] function. - -The `Package` class contains many of the most common -features you'll use when working with [classes in Dart][]. -This class has three members, -and defines a constructor and a method. - -The Dart language is [type safe][]; it uses -static type checking to ensure that -a variable's value always matches the -variable's static type. -When defining a class, annotating the members with -`String` is required, -but it is often optional due to type inference. -In the `main` function in this example -there are many lines that start with `final variableName =`. -These lines are type safe, -despite not being explicitly given a type. - -Dart also has built-in [sound null safety][]. -In the example, the `description` member is -declared with the type `String?`. -The `?` at the end of `String?` means that -this property can be null. -The other two members cannot be null, -and the program will not compile if -you tried to set them to `null`. -You can see this demonstrated in the constructor for -the `Package` class. It takes two required, -positional arguments and one optional, named argument. - -Next in the example is the `main` function. -All Dart programs, including Flutter apps, -start with a `main` function. -The function showcases several basic Dart language features, -including using libraries, marking functions as async, -making function calls, using `if` statement control-flow, -and more. - -:::note Where does initialization code go? -The main entrypoint in a starter -Flutter app is in `lib/main.dart`. -The default `main` method looks -like the following: - -```dart title="lib/main.dart" -void main() { - runApp(const MyApp()); -} -``` - -Perform any _quick_ initialization (less than a frame or two, roughly 16-32ms) -_before_ calling `runApp()`, -though be aware that the widget tree hasn't been created yet. -If you want to perform initialization that takes awhile, -such as loading data from disk or over a network, -do it in a way that won't block the main UI thread. -For more information, check out [Asynchronous programming][], -the [`FutureBuilder`][] API, [Deferred components][], -or the [Working with long lists][] cookbook recipe, -as appropriate. - -Every stateful widget has an `initState()` -method that is called when the widget is -created and added to the widget tree. -You can override this method and perform -initialization there, though the first line of -this method _must_ be `super.initState()`. - -Finally, hot reloading your app does _not_ -call `initState` or `main` again. -Hot restart calls both. -::: - -If these features aren't familiar to you, -you can find resources to learn Dart on the -[Bootstrap into Dart][] page. - -## Next: Widgets - -This page is an introduction to Dart, -and helps you become familiar with reading -Flutter and Dart code. It's okay if you don't -feel clear on all the code on this page, -as long as you feel comfortable with the _syntax_ -of the Dart language. -In the next section, you'll learn about the -building block of Flutter apps: widgets. - -[Asynchronous programming]: {{site.dart-site}}/libraries/async/async-await -[Dart]: {{site.dart-site}} -[Deferred components]: /perf/deferred-components -[`main`]: {{site.dart-site}}/language#hello-world -[classes in Dart]: {{site.dart-site}}/language/classes -[`FutureBuilder`]: {{site.api}}/flutter/widgets/FutureBuilder-class.html -[type safe]: {{site.dart-site}}/language/type-system -[sound null safety]: {{site.dart-site}}/null-safety -[Working with long lists]: /cookbook/lists/long-lists -[Bootstrap into Dart]: /resources/bootstrap-into-dart - -## Feedback - -As this section of the website is evolving, -we [welcome your feedback][]! - -[welcome your feedback]: https://google.qualtrics.com/jfe/form/SV_6A9KxXR7XmMrNsy?page="dart" diff --git a/src/content/get-started/fundamentals/index.md b/src/content/get-started/fundamentals/index.md deleted file mode 100644 index f76f1fe6b96..00000000000 --- a/src/content/get-started/fundamentals/index.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Learn the fundamentals -shortTitle: Fundamentals -description: > - You've gotten a taste of using the Flutter framework; - now go beyond to learn the basics of Flutter. -showToc: false ---- - - - -
    -
    - -## Find your way with Flutter! - -If you are new to Flutter, and have already worked -through [your first Flutter codelab][], -this section of the website is for you! - -The goal here is to guide you through some next -steps of learning Flutter. It's not about teaching -you how to _program_, it's about teaching you -how Flutter works. - - -
    -
    - -
    -
    - - -:::note -These fundamentals docs are still a work -in progress and we welcome your feedback! -Please consider filling out the survey -listed at the bottom of this page and on the -new subject pages in this section. -::: - -We suggest that you work through the -following subjects in the listed order. - - 1. [Intro to Dart][] _(Optional)_ - As you might know, Flutter uses the [Dart language][]. - If you have experience with other object-oriented - languages, like Java, C++, or Swift, - Dart should feel familiar to you. - As of this writing, - [Dart is one of the fastest growing languages][dart-lang], - in part, thanks to Flutter. - 2. [Widget fundamentals][] - Learn about one of the primary building blocks - of a Flutter application, widgets. - 3. [Layout][] - Flutter is different from other UI frameworks - in that you create the layout programmatically. - This allows you to compose widgets, - Flutter's basic building blocks, - to realize your own layout vision. - It also facilitates designing a UI to - optimize any screen where your app might be used. - 4. [State management][] - Learn how to share state between widgets and notify other parts of your app - when the state changes. - See how to implement MVVM in Flutter to manage state effectively - for small to medium-sized apps. - 5. [Handling user input][] - Learn about Flutter's widgets that support - interactivity, like buttons and text. - Also, learn how to add interactivity to - a widget that doesn't already support it. - 6. [Networking and data][] - Networking is a very large topic, - so this section focuses on basic networking - functionality, such as how to retrieve - or submit data using HTTP, - how to convert to and from JSON, - how to use authentication, - how to implement asynchronicity, and more. - 7. [Local data and caching][] - Learn about different techniques for caching - local data. - -[Dart language]: {{site.dart-site}} -[dart-lang]: https://twitter.com/MiSvTh/status/1732002450641400276?cxt -[Intro to Dart]: /get-started/fundamentals/dart -[Layout]: /get-started/fundamentals/layout -[State management]: /get-started/fundamentals/state-management -[Handling user input]: /get-started/fundamentals/user-input -[Networking and data]: /get-started/fundamentals/networking -[Local data and caching]: /get-started/fundamentals/local-caching -[Widget fundamentals]: /get-started/fundamentals/widgets -[your first Flutter codelab]: {{site.codelabs}}/codelabs/flutter-codelab-first - -## Feedback - -As this section of the website is evolving, -we [welcome your feedback][]! - -[welcome your feedback]: https://google.qualtrics.com/jfe/form/SV_6A9KxXR7XmMrNsy?page="index" diff --git a/src/content/get-started/fundamentals/layout.md b/src/content/get-started/fundamentals/layout.md deleted file mode 100644 index 806e4d4fa0b..00000000000 --- a/src/content/get-started/fundamentals/layout.md +++ /dev/null @@ -1,828 +0,0 @@ ---- -title: Layouts -description: Learn how to create layouts in Flutter. -prev: - title: Widgets - path: /get-started/fundamentals/widgets -next: - title: State management - path: /get-started/fundamentals/state-management ---- - -Given that Flutter is a UI toolkit, -you'll spend a lot of time creating layouts -with Flutter widgets. In this section, -you'll learn how to build layouts with some of the -most common layout widgets. -You'll use Flutter DevTools (also -called Dart DevTools) to understand how -Flutter is creating your layout. -Finally, you'll encounter and debug one of -Flutter's most common layout errors, -the dreaded "unbounded constraints" error. - -## Understanding layout in Flutter - -The core of Flutter's layout mechanism is widgets. -In Flutter, almost everything is a widget — even -layout models are widgets. -The images, icons, and text that you see in a -Flutter app are all widgets. -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 diagram below shows 3 icons with a label under -each one, and the corresponding widget tree: - -A diagram that shows widget composition with a series of lines and nodes. - -In this example, there's a row of 3 columns where -each column contains an icon and a label. -All layouts, no matter how complex, -are created by composing these layout widgets. - -### Constraints - -Understanding constraints in Flutter is an -important part of understanding -how layout works in Flutter. - -Layout, in a general sense, refers to the size of -the widgets and their positions on the screen. -The size and position of any given widget is -constrained by its parent; -it can't have any size it wants, -and it doesn't decide its own place on the screen. -Instead, size and position are determined by -a conversation between a widget and its parent. - -In the simplest example, -the layout conversation looks like this: - - 1. A widget receives its constraints from its parent. - 2. A constraint is just a set of 4 doubles: - a minimum and maximum width, - and a minimum and maximum height. - 3. The widget determines what size it should be - within those constraints, and passes its - width and height back to the parent. - 4. The parent looks at the size it wants to be and - how it should be aligned, - and sets the widget's position accordingly. - Alignment can be set explicitly, - using a variety of widgets like `Center`, - and the alignment properties on `Row` and `Column`. - -In Flutter, this layout conversation is often -expressed with the simplified phrase, -"Constraints go down. Sizes go up. -Parent sets the position." - -### Box types - -In Flutter, widgets are rendered by their -underlying [`RenderBox`][] objects. -These objects determine how to handle the -constraints they're passed. - -Generally, there are three kinds of boxes: -* Those that try to be as big as possible. -For example, the boxes used by -[`Center`][] and [`ListView`][]. -* Those that try to be the same size as their -children. For example, the boxes used by -[`Transform`][] and [`Opacity`][] -* Those that try to be a particular size. -For example, the boxes used by -[`Image`][] and [`Text`][]. - -Some widgets, for example [`Container`][], -vary from type to type based on their -constructor arguments. -The `Container` constructor defaults to 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. -Read more about flex boxes and constraints in -the [Understanding Constraints article][]. - -## Lay out a single widget - -To lay out a single widget in Flutter, -wrap a visible widget, -such as `Text` or `Image` with a widget that -can change its position on a screen, -such as a `Center` widget. - -:::note Note -The examples on the page use a widget called -`BorderedImage`. This is a custom widget, -and is used here to hide -the code that isn't relevant to this topic. -::: - -```dart -Widget build(BuildContext context) { - return Center( - child: BorderedImage(), - ); -} -``` - -The following figure shows a widget that isn't -aligned on the left, -and a widget that has been centered on the right. - -A screenshot of a centered widget and a screenshot of a widget that hasn't been centered. - -All layout widgets have either of the following: -* A `child` property if they take a single -child—for example, `Center`, `Container`, -or `Padding`. -* A `children` property if they take a list -of widgets—for example, -`Row`, `Column`, `ListView`, or `Stack`. - -### Container - -`Container` is a convenience widget that's -made up of several widgets responsible for layout, -painting, positioning, and sizing. -In regard to layout, -it can be used to add padding and -margins to a widget. -There is also a `Padding` widget -that could be used here to the same effect. -The following example uses a `Container`. - -```dart -Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.all(16.0), - child: BorderedImage(), - ); -} -``` - -The following figure shows a widget without -padding on the left, -and a widget with padding on the right. - -A screenshot of a widget with padding and a screenshot of a widget without padding. - -To create more complex layouts in Flutter, -you can compose many widgets. -For example, you can combine `Container` and `Center`: - -```dart -Widget build(BuildContext context) { - return Center( - Container( - padding: EdgeInsets.all(16.0), - child: BorderedImage(), - ), - ); -} -``` - -## Layout multiple widgets vertically or horizontally - -One of the most common layout patterns is to -arrange widgets vertically or horizontally. -You can use a `Row` widget to arrange widgets -horizontally, -and a `Column` widget to arrange widgets vertically. -The first figure on this page used both. - -This is the most basic example of using a `Row` widget. - - - - - - ```dart - Widget build(BuildContext context) { - return Row( - children: [ - BorderedImage(), - BorderedImage(), - BorderedImage(), - ], - ); - } - ``` - - - -Each child of `Row` or `Column` can be -rows and columns themselves, -combining to make a complex layout. -For example, you could add labels to each -of the images in the example above using columns. - - - - - - ```dart - Widget build(BuildContext context) { - return Row( - children: [ - Column( - children: [ - BorderedImage(), - Text('Dash 1'), - ], - ), - Column( - children: [ - BorderedImage(), - Text('Dash 2'), - ], - ), - Column( - children: [ - BorderedImage(), - Text('Dash 3'), - ], - ), - ], - ); - } - ``` - - - -### Align widgets within rows and columns - -In the following example, -the widgets are each 200 pixels wide, -and the viewport is 700 pixels wide. -The widgets are consequently aligned to the left, -one after the other, -with all the extra space on the right. - -A diagram that shows three widgets laid out in a row. Each child widget is labeled as 200px wide, and the blank space on the right is labeled as 100px wide. - -You control how a row or column aligns its -children using the `mainAxisAlignment` and -`crossAxisAlignment` properties. -For a row, the main axis runs horizontally and -the cross axis runs vertically. For a column, -the main axis runs -vertically and the cross axis runs horizontally. - -A diagram that shows the direction of the main axis and cross axis in both rows and columns - -Setting the main axis alignment to `spaceEvenly` -divides the free horizontal space evenly between, -before, and after each image. - - - - - - ```dart - Widget build(BuildContext context) { - return Row( - [!mainAxisAlignment: MainAxisAlignment.spaceEvenly!], - children: [ - BorderedImage(), - BorderedImage(), - BorderedImage(), - ], - ); - } - ``` - - - -Columns work the same way as rows. -The following example shows a column of 3 images, -each is 100 pixels high. The height of the -render box (in this case, the entire screen) -is more than 300 pixels, -so setting the main axis alignment to `spaceEvenly` -divides the free vertical space evenly between, -above, and below each image. - -A screenshot of a three widgets laid out vertically, using a column widget. - -The [`MainAxisAlignment`][] and [`CrossAxisAlignment`][] -enums offer a variety of constants for -controlling alignment. - -Flutter includes other widgets that can be used -for alignment, notably the `Align` widget. - -### Sizing widgets within rows and columns - -When a layout is too large to fit a device, -a yellow and black striped pattern appears -along the affected edge. -In this example, the viewport is 400 pixels wide, -and each child is 150 pixels wide. - -A screenshot of a row of widgets that are wider than their viewport. - -Widgets can be sized to fit within a -row or column by using the `Expanded` widget. -To fix the previous example where the row of -images is too wide for its render box, -wrap each image with an [`Expanded`][] widget. - - - - - - ```dart - Widget build(BuildContext context) { - return const Row( - children: [ - [!Expanded!]( - child: BorderedImage(width: 150, height: 150), - ), - [!Expanded!]( - child: BorderedImage(width: 150, height: 150), - ), - [!Expanded!]( - child: BorderedImage(width: 150, height: 150), - ), - ], - ); - } - ``` - - - -The `Expanded` widget can also dictate how much -space a widget should take up relative -to its siblings. For example, -perhaps you want a widget to occupy twice -as much space as its siblings. -For this, use the `Expanded` widgets `flex` property, -an integer that determines the flex factor -for a widget. The default flex factor is 1. -The following code sets the flex factor of the -middle image to 2: - - - - - - ```dart - Widget build(BuildContext context) { - return const Row( - children: [ - Expanded( - child: BorderedImage(width: 150, height: 150), - ), - Expanded( - [!flex: 2!], - child: BorderedImage(width: 150, height: 150), - ), - Expanded( - child: BorderedImage(width: 150, height: 150), - ), - ], - ); - } - ``` - - - -## DevTools and debugging layout - -In certain situations, -a box's constraint is unbounded, or infinite. -This means that either the maximum width or the -maximum height is set to [`double.infinity`][]. -A box that tries to be as big as possible won't -function usefully when given an -unbounded constraint and, in debug mode, -throws an exception. - -The most common case where a render box ends up -with 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 available in its cross-direction -(perhaps it's a vertically-scrolling -block and tries to be as wide as its parent). -If you nest a vertically scrolling `ListView` -inside 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. - -Perhaps the most common error you'll run into -while building a Flutter application is due to -incorrectly using layout widgets, -and is referred to as the "unbounded constraints" -error. - -If there was only one type error you should be -prepared to confront when you first start building -Flutter apps, it would be this one. - - - -:::note The Widget inspector -Flutter has a robust suite of DevTools that -help you work with any number of aspects of -Flutter development. -The "Widget Inspector" tool is particularly -useful when building and debugging layouts (and working with widgets in general). - -[Learn more about the Flutter inspector][]. -::: - -## Scrolling widgets - -Flutter has many built-in widgets that -automatically scroll and also offers a variety of -widgets that you can customize to -create specific scrolling behavior. -On this page, you'll see how to use the most common widget for -making any page scrollable, -as well as a widget for creating scrollable lists. - -### ListView - -`ListView` is a column-like widget that -automatically provides scrolling when its -content is longer than its render box. -The most basic way to use a `ListView` is -very similar to using a `Column` or `Row`. -Unlike a column or row, -a `ListView` requires its children to take up -all the available space on the cross axis, -as shown in the example below. - - - - - - ```dart - Widget build(BuildContext context) { - return [!ListView!]( - children: const [ - BorderedImage(), - BorderedImage(), - BorderedImage(), - ], - ); - } - ``` - - - -`ListView`s are commonly used when you have an -unknown or very large (or infinite) number of list items. -When this is the case, -it's best to use the `ListView.builder` constructor. -The builder constructor only builds the -children that are currently visible on screen. - -In the following example, -the `ListView` is displaying a list of to-do items. -The todo items are being fetched from a repository, -and therefore the number of todos is unknown. - - - - - - ```dart - final List items = Repository.fetchTodos(); - - Widget build(BuildContext context) { - return ListView.builder( - itemCount: items.length, - itemBuilder: (context, idx) { - var item = items[idx]; - return Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(item.description), - Text(item.isComplete), - ], - ), - ); - }, - ); - } - ``` - - - -## Adaptive layouts - -Because Flutter is used to create mobile, -tablet, desktop, _and_ web apps, -it's likely you'll need to adjust your -application to behave differently depending on -things like screen size or input device. -This is referred to as making an app -_adaptive_ and _responsive_. - -One of the most useful widgets in making -adaptive layouts is the [`LayoutBuilder`][] widget. -`LayoutBuilder` is one of many widgets that uses -the "builder" pattern in Flutter. - -### The builder pattern - -In Flutter, you'll find several widgets that use -the word "builder" in their names or -in their constructors. -The following list isn't exhaustive: - -* [`ListView.builder`][] -* [`GridView.builder`][] -* [`Builder`][] -* [`LayoutBuilder`][] -* [`FutureBuilder`][] - -These different "builders" are useful for solving -different problems. For example, -the `ListView.builder` constructor is primarily used -to lazily render items in a list, -while the `Builder` widget is useful for gaining -access to the `BuildContext` in deeply widget code. - -Despite their different use cases, -these builders are unified by how they work. -Builder widgets and builder constructors all have -arguments called 'builder' -(or something similar, -like `itemBuilder` in the case of `ListView.builder`), -and the builder argument always accepts a -callback. -This callback is a __builder function__. -Builder functions are callbacks that pass data to -the parent widget, -and the parent widget uses those arguments to -build and return the child widget. -Builder functions always pass in at least -one argument–the build context– -and generally at least one other argument. - -For example, the `LayoutBuilder` widget is used -to create responsive layouts based -on the size of the viewport. The builder callback -body is passed the [`BoxConstraints`][] that it receives -from its parent, along with the widgets 'BuildContext'. -With these constraints, you can return a different -widget based on the available space. - - - -In the following example, -the widget returned by the `LayoutBuilder` -changes based on whether the viewport is -less than or equal 600 pixels, -or greater than 600 pixels. - - - - - - ```dart - Widget build(BuildContext context) { - return LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - [!if (constraints.maxWidth <= 600)!] { - return _MobileLayout(); - } else { - return _DesktopLayout(); - } - }, - ); - } - ``` - - - -Meanwhile, the `itemBuilder` callback on the -`ListView.builder` constructor is passed the -build context and an `int`. -This callback is called once for every item -in the list, -and the int argument represents the index of the list item. -The first time the itemBuilder callback is called -when Flutter is building the UI, -the int passed to the function is 0, -the second time it's 1, and so on. - -This allows you to provide specific configuration -based on the index. Recall the example above using -the`ListView.builder` constructor: - -```dart -final List items = Repository.fetchTodos(); - -Widget build(BuildContext context) { - return ListView.builder( - itemCount: items.length, - itemBuilder: (context, idx) { - var item = items[idx]; - return Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(item.description), - Text(item.isComplete), - ], - ), - ); - }, - ); -} -``` - -This example code uses the index that's -passed into the builder to grab the correct -todo from the list of items, -and then displays that todo's data in -the widget that is returned from the builder. - -To exemplify this, -the following example changes the -background color of every other list item. - - - - - - ```dart - final List items = Repository.fetchTodos(); - - Widget build(BuildContext context) { - return ListView.builder( - itemCount: items.length, - itemBuilder: (context, idx) { - var item = items[idx]; - return Container( - [!color: idx % 2 == 0 ? Colors.lightBlue : Colors.transparent!], - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(item.description), - Text(item.isComplete), - ], - ), - ); - }, - ); - } - ``` - - - -## Additional resources - -* Common layout widgets and concepts - * Video: [OverlayPortal—Flutter Widget of the Week][] - * Video: [Stack—Flutter Widget of the Week][] - * Tutorial: [Layouts in Flutter][] - * Documentation: [Stack documentation][] -* Sizing and positioning widgets - * Video: [Expanded—Flutter Widget of the Week][] - * Video: [Flexible—Flutter Widget of the Week][] - * Video: [Intrinsic widgets—Decoding Flutter][] -* Scrollable widgets - * Example code: [Work with long lists][] - * Example code: [Create a horizontal list][] - * Example code: [Create a grid list][] - * Video: [ListView—Flutter Widget of the Week][] -* Adaptive Apps - * Tutorial: [Adaptive Apps codelab][] - * Video: [MediaQuery—Flutter Widget of the Week][] - * Video: [Building platform adaptive apps][] - * Video: [Builder—Flutter Widget of the Week][] - -### API reference - -The following resources explain individual APIs. - -* [`Builder`][] -* [`Row`][] -* [`Column`][] -* [`Expanded`][] -* [`Flexible`][] -* [`ListView`][] -* [`Stack`][] -* [`Positioned`][] -* [`MediaQuery`][] -* [`LayoutBuilder`][] - -[Layouts in Flutter]: /ui/layout -[Understanding constraints article]: /ui/layout/constraints -[`RenderBox`]: {{site.api}}/flutter/rendering/RenderBox-class.html -[Expanded—Flutter Widget of the Week]: {{site.youtube-site}}/watch?v=_rnZaagadyo -[Flexible—Flutter Widget of the Week]: {{site.youtube-site}}/watch?v=CI7x0mAZiY0 -[Intrinsic widgets—Decoding Flutter]: {{site.youtube-site}}/watch?v=Si5XJ_IocEs -[Build a Flutter Layout]: /ui/layout/tutorial -[Basic scrolling]: /ui/layout/scrolling#basic-scrolling -[Builder—Flutter Widget of the Week]: {{site.youtube-site}}/watch?v=xXNOkIuSYuA -[ListView—Flutter Widget of the Week]: {{site.youtube-site}}/watch?v=KJpkjHGiI5A -[Work with long lists]: /cookbook/lists/long-lists -[Create a horizontal list]: /cookbook/lists/horizontal-list -[Create a grid list]: /cookbook/lists/grid-lists -[PageView—Flutter Widget of the Week]: {{site.youtube-site}}/watch?v=J1gE9xvph-A -[Stack—Flutter Widget of the Week]: {{site.youtube-site}}/watch?v=liEGSeD3Zt8 -[Stack documentation]: /ui/layout#stack -[OverlayPortal—Flutter Widget of the Week]: {{site.youtube-site}}/watch?v=S0Ylpa44OAQ -[LayoutBuilder—Flutter Widget of the Week]: {{site.youtube-site}}/watch?v=IYDVcriKjsw -[MediaQuery—Flutter Widget of the Week]: {{site.youtube-site}}/watch?v=A3WrA4zAaPw -[Adaptive apps codelab]: {{site.codelabs}}/codelabs/flutter-adaptive-app -[Building platform adaptive apps]: {{site.youtube-site}}/watch?v=RCdeSKVt7LI -[Learn more about the Flutter inspector]: /tools/devtools/inspector -[Unbounded height and width—Decoding Flutter]: {{site.youtube-site}}/watch?v=jckqXR5CrPI -[2D Scrolling]: {{site.youtube-site}}/watch?v=ppEdTo-VGcg -[`Builder`]: {{site.api}}/flutter/widgets/Builder-class.html -[`Row`]: {{site.api}}/flutter/widgets/Row-class.html -[`Column`]: {{site.api}}/flutter/widgets/Column-class.html -[`Expanded`]: {{site.api}}/flutter/widgets/Expanded-class.html -[`Flexible`]: {{site.api}}/flutter/widgets/Flexible-class.html -[`ListView`]: {{site.api}}/flutter/widgets/ListView-class.html -[`Stack`]: {{site.api}}/flutter/widgets/Stack-class.html -[`Positioned`]: {{site.api}}/flutter/widgets/Positioned-class.html -[`MediaQuery`]: {{site.api}}/flutter/widgets/MediaQuery-class.html -[`Transform`]:{{site.api}}/flutter/widgets/Transform-class.html -[`Opacity`]:{{site.api}}/flutter/widgets/Opacity-class.html -[`Center`]:{{site.api}}/flutter/widgets/Center-class.html -[`ListView`]:{{site.api}}/flutter/widgets/Listview-class.html -[`Image`]:{{site.api}}/flutter/widgets/Image-class.html -[`Text`]:{{site.api}}/flutter/widgets/Text-class.html -[`MainAxisAlignment`]: {{site.api}}/flutter/rendering/MainAxisAlignment.html -[`CrossAxisAlignment`]: {{site.api}}/flutter/rendering/CrossAxisAlignment.html -[`double.infinity`]:{{site.api}}/flutter/dart-core/double/infinity-constant.html -[`ListView.builder`]: {{site.api}}/flutter/widgets/ListView/ListView.builder.html -[`GridView.builder`]: {{site.api}}/flutter/widgets/GridView/GridView.builder.html -[`Builder`]: {{site.api}}/flutter/widgets/Builder-class.html -[`ScrollView`]: {{site.api}}/flutter/widgets/Scrollview-class.html -[`LayoutBuilder`]: {{site.api}}/flutter/widgets/LayoutBuilder-class.html -[`BoxConstraints`]:{{site.api}}/flutter/rendering/BoxConstraints-class.html -[`LayoutBuilder`]: {{site.api}}/flutter/widgets/LayoutBuilder-class.html -[`FutureBuilder`]: {{site.api}}/flutter/widgets/FutureBuilder-class.html -[`Container`]:{{site.api}}/flutter/widgets/Container-class.html -[`Column`]:{{site.api}}/flutter/widgets/Column-class.html -[`Row`]:{{site.api}}/flutter/widgets/Row-class.html -[`Expanded`]: {{site.api}}/flutter/widgets/Expanded-class.html - -## Feedback - -As this section of the website is evolving, -we [welcome your feedback][]! - -[welcome your feedback]: https://google.qualtrics.com/jfe/form/SV_6A9KxXR7XmMrNsy?page="layout" diff --git a/src/content/get-started/fundamentals/local-caching.md b/src/content/get-started/fundamentals/local-caching.md deleted file mode 100644 index 39df4b82798..00000000000 --- a/src/content/get-started/fundamentals/local-caching.md +++ /dev/null @@ -1,253 +0,0 @@ ---- -title: Local caching -description: Learn how to persist data locally. -prev: - title: Networking and data - path: /get-started/fundamentals/networking -next: - title: Learn more - path: /get-started/learn-flutter ---- - -Now that you've learned about how to load data from servers -over the network, your Flutter app should feel more alive. -However, just because you *can* load data from remote servers -doesn't mean you always *should*. Sometimes, it's better to -re-render the data you received from the previous network -request rather than repeat it and make your user wait until -it completes again. This technique of retaining application -data to show again at a future time is called *caching*, and -this page covers how to approach this task in your Flutter app. - -## Introduction to caching - -At its most basic, all caching strategies amount to the same -three-step operation, represented with the following pseudocode: - -```dart -Data? _cachedData; - -Future get data async { - // Step 1: Check whether your cache already contains the desired data - if (_cachedData == null) { - // Step 2: Load the data if the cache was empty - _cachedData = await _readData(); - } - // Step 3: Return the value in the cache - return _cachedData!; -} -``` - -There are many interesting ways to vary this strategy, -including the location of the cache, the extent to which you -preemptively write values to, or "warm", the cache; and others. - -## Common caching terminology - -Caching comes with its own terminology, some of which is -defined and explained below. - -**Cache hit** -: An app is said to have had a cache hit when the cache already - contained their desired information and loading it from the - real source of truth was unnecessary. - -**Cache miss** -: An app is said to have had a cache miss when the cache was - empty and the desired data is loaded from the real source - of truth, and then saved to the cache for future reads. - -## Risks of caching data - -An app is said to have a **stale cache** when the data within -the source of truth has changed, which puts the app at risk -of rendering old, outdated information. - -All caching strategies run the risk of holding onto stale data. -Unfortunately, the action of verifying the freshness of a cache -often takes as much time to complete as fully loading the data -in question. This means that most apps tend to only benefit -from caching data if they trust the data to be fresh at runtime -without verification. - -To deal with this, most caching systems include a time limit -on any individual piece of cached data. After this time limit -is exceeded, would-be cache hits are treated as cache misses -until fresh data is loaded. - -A popular joke among computer scientists is that "The two -hardest things in computer science are cache invalidation, -naming things, and off-by-one errors." 😄 - -Despite the risks, almost every app in the world makes heavy -use of data caching. The rest of this page explores multiple -approaches to caching data in your Flutter app, but know that -all of these approaches can be tweaked or combined for your -situation. - -## Caching data in local memory - -The simplest and most performant caching strategy is an -in-memory cache. The downside of this strategy is that, -because the cache is only held in system memory, no data is -retained beyond the session in which it is originally cached. -(Of course, this "downside" also has the upside of automatically -solving most stale cache problems!) - -Due to their simplicity, in-memory caches closely mimic -the pseudocode seen above. That said, it is best to use proven -design principles, like the [repository pattern][], -to organize your code and prevent cache checks like the above -from appearing all over your code base. - -Imagine a `UserRepository` class that is also tasked with -caching users in memory to avoid duplicate network requests. -Its implementation might look like this: - -```dart -class UserRepository { - UserRepository(this.api); - - final Api api; - final Map _userCache = {}; - - Future loadUser(int id) async { - if (!_userCache.containsKey(id)) { - final response = await api.get(id); - if (response.statusCode == 200) { - _userCache[id] = User.fromJson(response.body); - } else { - _userCache[id] = null; - } - } - return _userCache[id]; - } -} -``` - -This `UserRepository` follows multiple proven design -principles including: - -* [dependency injection][], which helps with testing -* [loose coupling][], which protects surrounding code from -its implementation details, and -* [separation of concerns][], which prevents its implementation -from juggling too many concerns. - -And best of all, no matter how many times within a single session -a user visits pages in your Flutter app that load a given user, -the `UserRepository` class only loads that data over the network *once*. - -However, your users might eventually tire of waiting for data -to load every time they relaunch your app. For that, you should -choose from one of the persistent caching strategies found below. - -[dependency injection]: https://en.wikipedia.org/wiki/Dependency_injection -[loose coupling]: https://en.wikipedia.org/wiki/Loose_coupling -[repository Pattern]: https://medium.com/@pererikbergman/repository-design-pattern-e28c0f3e4a30 -[separation of concerns]: https://en.wikipedia.org/wiki/Separation_of_concerns - -## Persistent caches - -Caching data in memory will never see your precious cache -outlive a single user session. -To enjoy the performance benefits of cache hits on fresh -launches of your application, you need to cache data somewhere -on the device's hard drive. - -### Caching data with `shared_preferences` - -[`shared_preferences`][] is a Flutter plugin that wraps -platform-specific [key-value storage][] on all six of Flutter's -target platforms. -Although these underlying platform key-value stores were designed -for small data sizes, they are still suitable for a caching -strategy for most applications. -For a complete guide, see our other resources on using key-value stores. - -* Cookbook: [Store key-value data on disk][] -* Video: [Package of the Week: `shared_preferences`][] - -[key-value storage]: https://en.wikipedia.org/wiki/Key%E2%80%93value_database -[Package of the Week: `shared_preferences`]: https://www.youtube.com/watch?v=sa_U0jffQII -[`shared_preferences`]: {{site.pub-pkg}}/shared_preferences -[Store key-value data on disk]: /cookbook/persistence/key-value - -### Caching data with the file system - -If your Flutter app outgrows the low-throughput scenarios -ideal for `shared_preferences`, you might be ready to explore -caching data with your device's file system. -For a more thorough guide, see our other resources on -file system caching. - -* Cookbook: [Read and write files][] - -[Read and write files]: /cookbook/persistence/reading-writing-files - -### Caching data with an on-device database - -The final boss of local data caching is any strategy -that uses a proper database to read and write data. -Multiple flavors exist, including relational and -non-relational databases. -All approaches offer dramatically improved performance over -simple files - especially for large datasets. -For a more thorough guide, see the following resources: - -* Cookbook: [Persist data with SQLite][] -* SQLite alternate: [`sqlite3` package][] -* Drift, a relational database: [`drift` package][] -* Hive CE, a non-relational database: [`hive_ce` package][] -* Isar Community, a fast non-relational database: [`isar_community` package][] -* Remote Caching, a lightweight caching system for API responses: [`remote_caching` package][] - -[`drift` package]: {{site.pub-pkg}}/drift -[`hive_ce` package]: {{site.pub-pkg}}/hive_ce -[`isar_community` package]: {{site.pub-pkg}}/isar_community -[`remote_caching` package]: {{site.pub-pkg}}/remote_caching - -[Persist data with SQLite]: /cookbook/persistence/sqlite -[`sqlite3` package]: {{site.pub-pkg}}/sqlite3 - -## Caching images - -Caching images is a similar problem space to caching regular data, -though with a one-size-fits-all solution. -To direct your Flutter app to use the file system to store images, -use the [`cached_network_image` package][]. - -* Video: [Package of the Week: `cached_network_image`][] - -{% comment %} -TODO: My understanding is that we now recommend `Image.network` instead of cache_network_image. -{% endcomment %} - -[`cached_network_image` package]: {{site.pub-pkg}}/cached_network_image -[Package of the Week: `cached_network_image`]: https://www.youtube.com/watch?v=fnHr_rsQwDA - -## State restoration - -Along with application data, you might also want to persist other -aspects of a user's session, like their navigation stack, scroll -positions, and even partial progress filling out forms. This -pattern is called "state restoration", and is built in to Flutter. - -State restoration works by instructing the Flutter framework -to sync data from its Element tree with the Flutter engine, -which then caches it in platform-specific storage for future -sessions. To enable state restoration on Flutter for Android -and iOS, see the following documentation: - -* Android documentation: [Android state restoration][] -* iOS documentation: [iOS state restoration][] - -[Android state restoration]: /platform-integration/android/restore-state-android -[iOS state restoration]: /platform-integration/ios/restore-state-ios - -## Feedback - -As this section of the website is evolving, -we [welcome your feedback][]! - -[welcome your feedback]: https://google.qualtrics.com/jfe/form/SV_6A9KxXR7XmMrNsy?page="local-caching" diff --git a/src/content/get-started/fundamentals/networking.md b/src/content/get-started/fundamentals/networking.md deleted file mode 100644 index 717f4db488b..00000000000 --- a/src/content/get-started/fundamentals/networking.md +++ /dev/null @@ -1,145 +0,0 @@ ---- -title: Networking and data -description: Learn how to network your Flutter app. -prev: - title: Handling user input - path: /get-started/fundamentals/user-input -next: - title: Local data and caching - path: /get-started/fundamentals/local-caching ---- - -While it's said that "no man is an island", -a Flutter app without any networking capability -can feel a tad disconnected. -This page covers how to add networking features -to your Flutter app. Your app will retrieve data, -parse JSON into usable in memory representations, -and then send data out again. - -## Introduction to retrieving data over the network - -At it's simplest, assuming you utilize the [`http`][] -package to adapt to the differences between network access -from Dart VM based platforms and web browser-based environments, -making a HTTP `GET` request can be as simple as the following: - -```dart -import 'package:http/http.dart' as http; - -void main() async { - var response = await http.get( - Uri.parse('https://jsonplaceholder.typicode.com/albums/1'), - ); - print(response.body); -} -``` - -The following two tutorials show you all of the details -involved in adding the [`http`][] package to your app, -whether you are running on Android, -iOS, inside a web browser, or natively on Windows, -macOS, or Linux. -The first tutorial shows you how to make an -unauthenticated `GET` request to a website, -parse the retrieved data as `JSON` and then -display the resulting data. The second tutorial -builds on the first by adding authentication headers, -enabling access to web servers requiring authorization. -The article by the Mozilla Developer Network (MDN) -gives more background on how authorization works on the web. - -* Tutorial: [Fetch data from the internet][] -* Tutorial: [Make authenticated requests][] -* Article: [MDN's article on Authorization for websites][] - -## Making data retrieved from the network useful - -Once you retrieve data from the network, -you need a way to convert the data from the network -into something that you can easily work with in Dart. -The tutorials in the previous section used hand rolled Dart -to convert network data into an in-memory representation. -In this section, -you'll see other options for handling this conversion. -The first links to a YouTube video showing an overview -of the [`freezed` package][]. -The second links to a codelab that covers patterns -and records using a case study of parsing JSON. - -* YouTube video: [Freezed (Package of the Week)][] -* Codelab: [Dive into Dart's patterns and records][] - -## Going both ways, getting data out again - -Now that you've mastered the art of retrieving data, -it's time to look at pushing data out. -This information starts with sending data to the network, -but then dives into asynchronicity. The truth is, -once you are in a conversation over the network, -you'll need to deal with the fact that web servers -that are physically far away can take a while to respond, -and you can't stop rendering to the screen -while you wait for packets to round trip. -Dart has great support for asynchronicity, -as does Flutter. -You'll learn all about Dart's support in a tutorial, -then see Flutter's capability covered in a -Widget of the Week video. -Once you complete that, you'll learn how to debug -network traffic using DevTool's Network View. - -* Tutorial: [Send data to the internet][] -* Tutorial: [Asynchronous programming: futures, async, await][] -* YouTube video: [FutureBuilder (Widget of the Week)][] -* Article: [Using the Network View][] - -## Extension material - -Now that you've mastered using Flutter's networking APIs, -it helps to see Flutter's network usage in context. -The first codelab (ostensibly on creating Adaptive apps in Flutter), -uses a web server written in Dart to work around the web browsers' -[Cross-Origin Resource Sharing (CORS) restrictions][]. - -:::note -If you've already worked through this codelab -on the [layout][] page, feel free to skip this step. -::: - -[layout]: /get-started/fundamentals/layout - -Next, a long-form YouTube video where -Flutter DevRel alumnus, Fitz, -talks about how the location of data matters for Flutter apps. -Finally, a really useful series of articles by Flutter GDE -Anna (Domashych) Leushchenko covering advanced networking in Flutter. - -* Codelab: [Adaptive apps in Flutter][] -* Video: [Keeping it local: Managing a Flutter app's data][] -* Article series: [Basic and advanced networking in Dart and Flutter][] - - -[Adaptive apps in Flutter]: {{site.codelabs}}/codelabs/flutter-adaptive-app -[Asynchronous programming: futures, async, await]: {{site.dart-site}}/codelabs/async-await -[Basic and advanced networking in Dart and Flutter]: {{site.medium}}/tide-engineering-team/basic-and-advanced-networking-in-dart-and-flutter-the-tide-way-part-0-introduction-33ac040a4a1c -[Cross-Origin Resource Sharing (CORS) restrictions]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS -[Dive into Dart's patterns and records]: {{site.codelabs}}/codelabs/dart-patterns-records -[Fetch data from the internet]: /cookbook/networking/fetch-data -[Freezed (Package of the Week)]: {{site.youtube-site}}/watch?v=RaThk0fiphA -[`freezed` package]: {{site.pub-pkg}}/freezed -[FutureBuilder (Widget of the Week)]: {{site.youtube-site}}/watch?v=zEdw_1B7JHY -[`http`]: {{site.pub-pkg}}/http -[HTTP]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview -[Keeping it local: Managing a Flutter app's data]: {{site.youtube-site}}/watch?v=uCbHxLA9t9E -[Make authenticated requests]: /cookbook/networking/authenticated-requests -[MDN's article on Authorization for websites]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization -[Using the Network View]: /tools/devtools/network -[Send data to the internet]: /cookbook/networking/send-data - -## Feedback - -As this section of the website is evolving, -we [welcome your feedback][]! - -[welcome your feedback]: https://google.qualtrics.com/jfe/form/SV_6A9KxXR7XmMrNsy?page="networking" diff --git a/src/content/get-started/fundamentals/state-management.md b/src/content/get-started/fundamentals/state-management.md deleted file mode 100644 index 2f13e979cb0..00000000000 --- a/src/content/get-started/fundamentals/state-management.md +++ /dev/null @@ -1,579 +0,0 @@ ---- -title: State management -description: Learn how to manage state in Flutter. -prev: - title: Layout - path: /get-started/fundamentals/layout -next: - title: Handling user input - path: /get-started/fundamentals/user-input ---- - -The _state_ of a Flutter app refers to all the objects it uses -to display its UI or manage system resources. -State management is how we organize our app -to most effectively access these objects -and share them between different widgets. - -This page explores many aspects of state management, including: - -* Using a [`StatefulWidget`][] -* Sharing state between widgets using constructors, - [`InheritedWidget`][]s, and callbacks -* Using [`Listenable`][]s to notify other widgets - when something changes -* Using Model-View-ViewModel (MVVM) - for your application's architecture - -For other introductions to state management, check out these resources: - -* Video: [Managing state in Flutter][managing-state-video]. - This video shows how to use the [riverpod][] package. - -* Tutorial: [State management][]. - This shows how to use `ChangeNotifer` with the [provider][] package. - -This guide doesn't use third-party packages -like provider or Riverpod. Instead, -it only uses primitives available in the Flutter framework. - -## Using a StatefulWidget - -The simplest way to manage state is to use a `StatefulWidget`, -which stores state within itself. -For example, consider the following widget: - -```dart -class MyCounter extends StatefulWidget { - const MyCounter({super.key}); - - @override - State createState() => _MyCounterState(); -} - -class _MyCounterState extends State { - int count = 0; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Text('Count: $count'), - TextButton( - onPressed: () { - setState(() { - count++; - }); - }, - child: Text('Increment'), - ) - ], - ); - } -} -``` - -This code illustrates two important concepts -when thinking about state management: - -* **Encapsulation** -: The widget that uses `MyCounter` has no visibility into - the underlying `count` variable - and no means to access or change it. -* **Object lifecycle** -: The `_MyCounterState` object and its `count` variable - are created the first time that `MyCounter` is built, - and exist until it's removed from the screen. - This is an example of _ephemeral state_. - -You might find the following resources to be useful: - -* Article: [Ephemeral state and app state][ephemeral-state] -* API docs: [StatefulWidget][] - -## Sharing state between widgets - -Some scenarios where an app needs to store state -include the following: - -* To **update** the shared state and notify other parts of the app -* To **listen** for changes to the shared state - and rebuild the UI when it changes - -This section explores how you can effectively share state -between different widgets in your app. -The most common patterns are: - -* **Using widget constructors** - (sometimes called "prop drilling" in other frameworks) -* **Using `InheritedWidget`** (or a similar API, - such as the [provider][] package). -* **Using callbacks** to notify a parent widget - that something has changed - -### Using widget constructors - -Since Dart objects are passed by reference, -it's very common for widgets to define the -objects they need to use in their constructor. -Any state you pass into a widget's constructor -can be used to build its UI: - -```dart -class MyCounter extends StatelessWidget { - final int count; - const MyCounter({super.key, required this.count}); - - @override - Widget build(BuildContext context) { - return Text('$count'); - } -} -``` - -This makes it obvious for other users of your widget to know -what they need to provide in order to use it: - -```dart -Column( - children: [ - MyCounter( - count: count, - ), - MyCounter( - count: count, - ), - TextButton( - child: Text('Increment'), - onPressed: () { - setState(() { - count++; - }); - }, - ) - ], -) -``` - -Passing the shared data for your app through widget constructors -makes it clear to anyone reading the code that there are shared dependencies. -Creating a chain of widgets that only pass data down to their children -is often referred to as [_prop drilling_](/resources/glossary#prop-drilling). - -### Using InheritedWidget - -Manually passing data down the widget tree can be verbose -and cause unwanted boilerplate code, -so Flutter provides _`InheritedWidget`_, -which provides a way to efficiently host data in a parent widget -so that child widgets can access them without storing them as a field. - -To use `InheritedWidget`, extend the `InheritedWidget` class -and implement the static method `of()` -using `dependOnInheritedWidgetOfExactType`. -A widget calling `of()` in a build method -creates a dependency that is managed by the Flutter framework, -so that any widgets that depend on this `InheritedWidget` rebuild -when this widget re-builds with new data -and `updateShouldNotify` returns true. - -```dart -class MyState extends InheritedWidget { - const MyState({ - super.key, - required this.data, - required super.child, - }); - - final String data; - - static MyState of(BuildContext context) { - // This method looks for the nearest `MyState` widget ancestor. - final result = context.dependOnInheritedWidgetOfExactType(); - - assert(result != null, 'No MyState found in context'); - - return result!; - } - - @override - // This method should return true if the old widget's data is different - // from this widget's data. If true, any widgets that depend on this widget - // by calling `of()` will be re-built. - bool updateShouldNotify(MyState oldWidget) => data != oldWidget.data; -} -``` - -Next, call the `of()` method -from the `build()`method of the widget -that needs access to the shared state: - -```dart -class HomeScreen extends StatelessWidget { - const HomeScreen({super.key}); - - @override - Widget build(BuildContext context) { - var data = MyState.of(context).data; - return Scaffold( - body: Center( - child: Text(data), - ), - ); - } -} -``` - - -### Using callbacks - -You can notify other widgets when a value changes by exposing a callback. -Flutter provides the `ValueChanged` type, -which declares a function callback with a single parameter: - -```dart -typedef ValueChanged = void Function(T value); -``` - -By exposing `onChanged` in your widget's constructor, -you provide a way for any widget that is using this widget -to respond when your widget calls `onChanged`. - -```dart -class MyCounter extends StatefulWidget { - const MyCounter({super.key, required this.onChanged}); - - final ValueChanged onChanged; - - @override - State createState() => _MyCounterState(); -} -``` - -For example, this widget might handle the `onPressed` callback, -and call `onChanged` with its latest internal state for the `count` variable: - -```dart -TextButton( - onPressed: () { - widget.onChanged(count++); - }, -), -``` - -### Dive deeper - -For more information on sharing state between widgets, -check out the following resources: - -* Article: [Flutter Architectural Overview—State management][architecture-state] -* Video: [Pragmatic state management][] -* Video: [InheritedWidgets][inherited-widget-video] -* Video: [A guide to Inherited Widgets][] -* Sample: [Provider shopper][] -* Sample: [Provider counter][] -* API Docs: [`InheritedWidget`][] - -## Using listenables - -Now that you've chosen how you want to share state in your app, -how do you update the UI when it changes? -How do you change the shared state in a way -that notifies other parts of the app? - -Flutter provides an abstract class called `Listenable` -that can update one or more listeners. -Some useful ways to use listenables are: - -* Use a `ChangeNotifier` and subscribe to it using a `ListenableBuilder` -* Use a `ValueNotifier` with a `ValueListenableBuilder` - -### ChangeNotifier - -To use `ChangeNotifier`, create a class that extends it, -and call `notifyListeners` whenever the class needs to notify its listeners. - -```dart -class CounterNotifier extends ChangeNotifier { - int _count = 0; - int get count => _count; - - void increment() { - _count++; - notifyListeners(); - } -} -``` - -Then pass it to `ListenableBuilder` -to ensure that the subtree returned by the `builder` function -is re-built whenever the `ChangeNotifier` updates its listeners. - -```dart -Column( - children: [ - ListenableBuilder( - listenable: counterNotifier, - builder: (context, child) { - return Text('counter: ${counterNotifier.count}'); - }, - ), - TextButton( - child: Text('Increment'), - onPressed: () { - counterNotifier.increment(); - }, - ), - ], -) -``` - -### ValueNotifier - -A [`ValueNotifier`][] is a simpler version of a `ChangeNotifier`, -that stores a single value. -It implements the `ValueListenable` and `Listenable` interfaces, -so it's compatible -with widgets such as `ListenableBuilder` and `ValueListenableBuilder`. -To use it, create an instance of `ValueNotifier` with the initial value: - -```dart -ValueNotifier counterNotifier = ValueNotifier(0); -``` - -Then use the `value` field to read or update the value, -and notify any listeners that the value has changed. -Because `ValueNotifier` extends `ChangeNotifier`, -it is also a `Listenable` and can be used with a `ListenableBuilder`. -But you can also use `ValueListenableBuilder`, -which provides the value in the `builder` callback: - -```dart -Column( - children: [ - ValueListenableBuilder( - valueListenable: counterNotifier, - builder: (context, value, child) { - return Text('counter: $value'); - }, - ), - TextButton( - child: Text('Increment'), - onPressed: () { - counterNotifier.value++; - }, - ), - ], -) -``` - -### Deep dive - -To learn more about `Listenable` objects, check out the following resources: - -* API Docs: [`Listenable`][] -* API Docs: [`ValueNotifier`][] -* API Docs: [`ValueListenable`][] -* API Docs: [`ChangeNotifier`][] -* API Docs: [`ListenableBuilder`][] -* API Docs: [`ValueListenableBuilder`][] -* API Docs: [`InheritedNotifier`][] - -## Using MVVM for your application's architecture - -Now that we understand how to share state -and notify other parts of the app when its state changes, -we're ready to start thinking about how to organize -the stateful objects in our app. - -This section describes how to implement a design pattern that works well -with reactive frameworks like Flutter, -called _Model-View-ViewModel_ or _MVVM_. - -### Defining the Model - -The Model is typically a Dart class that does low-level tasks -such as making HTTP requests, -caching data, or managing system resources such as a plugin. -A model doesn't usually need to import Flutter libraries. - -For example, consider a model that loads or updates the counter state -using an HTTP client: - -```dart -import 'package:http/http.dart'; - -class CounterData { - CounterData(this.count); - - final int count; -} - -class CounterModel { - Future loadCountFromServer() async { - final uri = Uri.parse('https://myfluttercounterapp.net/count'); - final response = await get(uri); - - if (response.statusCode != 200) { - throw ('Failed to update resource'); - } - - return CounterData(int.parse(response.body)); - } - - Future updateCountOnServer(int newCount) async { - // ... - } -} -``` - -This model doesn't use any Flutter primitives or make any assumptions -about the platform it's running on; -its only job is to fetch or update the count using its HTTP client. -This allows the model to be implemented with a Mock or Fake in unit tests, -and defines clear boundaries between your app's low-level components and the -higher-level UI components needed to build the full app. - -The `CounterData` class defines the structure of the data -and is the true "model" of our application. -The model layer is typically responsible for the core algorithms -and data structures needed for your app. -If you are interested in other ways to define the model, -such as using immutable value types, -check out packages like [freezed][] -or [build_collection][] on pub.dev. - -### Defining the ViewModel - -A `ViewModel` binds the _View_ to the _Model_. -It protects the model from being accessed directly by the View, -and ensures that data flow starts from a change to the model. -Data flow is handled by the `ViewModel`, which uses `notifyListeners` -to inform the View that something changed. -The `ViewModel` is like a waiter in a restaurant -that handles the communication -between the kitchen (model) and the customers (views). - -```dart -import 'package:flutter/foundation.dart'; - -class CounterViewModel extends ChangeNotifier { - final CounterModel model; - int? count; - String? errorMessage; - CounterViewModel(this.model); - - Future init() async { - try { - count = (await model.loadCountFromServer()).count; - } catch (e) { - errorMessage = 'Could not initialize counter'; - } - notifyListeners(); - } - - Future increment() async { - final currentCount = count; - if (currentCount == null) { - throw('Not initialized'); - } - try { - final incrementedCount = currentCount + 1; - await model.updateCountOnServer(incrementedCount); - count = incrementedCount; - } catch(e) { - errorMessage = 'Could not update count'; - } - notifyListeners(); - } -} -``` - -Notice that the `ViewModel` stores an `errorMessage` -when it receives an error from the Model. -This protects the View from unhandled runtime errors, -which could lead to a crash. -Instead, the `errorMessage` field -can be used by the view to show a user-friendly error message. - - -### Defining the View - -Since our `ViewModel` is a `ChangeNotifier`, -any widget with a reference to it can use a `ListenableBuilder` -to rebuild its widget tree -when the `ViewModel` notifies its listeners: - -```dart -ListenableBuilder( - listenable: viewModel, - builder: (context, child) { - return Column( - children: [ - if (viewModel.errorMessage != null) - Text( - 'Error: ${viewModel.errorMessage}', - style: Theme.of(context) - .textTheme - .labelSmall - ?.apply(color: Colors.red), - ), - Text('Count: ${viewModel.count}'), - TextButton( - onPressed: () { - viewModel.increment(); - }, - child: Text('Increment'), - ), - ], - ); - }, -) -``` - -This pattern allows the business logic of your application -to be separate from the UI logic -and low-level operations performed by the Model layer. - -## Learn more about state management - -This page touches the surface of state management as -there are many ways to organize and manage -the state of your Flutter application. -If you would like to learn more, check out the following resources: - -* Article: [List of state management approaches][] -* Repository: [Flutter Architecture Samples][] - -[A guide to Inherited Widgets]: {{site.youtube-site}}/watch?v=Zbm3hjPjQMk -[build_collection]: {{site.pub-pkg}}/built_collection -[Flutter Architecture Samples]: https://fluttersamples.com/ -[`InheritedWidget`]: {{site.api}}/flutter/widgets/InheritedWidget-class.html -[List of state management approaches]: /data-and-backend/state-mgmt/options -[Pragmatic state management]: {{site.youtube-site}}/watch?v=d_m5csmrf7I -[Provider counter]: https://github.com/flutter/samples/tree/main/provider_counter -[Provider shopper]: https://github.com/flutter/samples/tree/main/provider_shopper -[State management]: /data-and-backend/state-mgmt/intro -[StatefulWidget]: {{site.api}}/flutter/widgets/StatefulWidget-class.html -[`StatefulWidget`]: {{site.api}}/flutter/widgets/StatefulWidget-class.html -[`ChangeNotifier`]: {{site.api}}/flutter/foundation/ChangeNotifier-class.html -[`InheritedNotifier`]: {{site.api}}/flutter/widgets/InheritedNotifier-class.html -[`ListenableBuilder`]: {{site.api}}/flutter/widgets/ListenableBuilder-class.html -[`Listenable`]: {{site.api}}/flutter/foundation/Listenable-class.html -[`ValueListenableBuilder`]: {{site.api}}/flutter/widgets/ValueListenableBuilder-class.html -[`ValueListenable`]: {{site.api}}/flutter/foundation/ValueListenable-class.html -[`ValueNotifier`]: {{site.api}}/flutter/foundation/ValueNotifier-class.html -[architecture-state]: /resources/architectural-overview#state-management -[ephemeral-state]: /data-and-backend/state-mgmt/ephemeral-vs-app -[freezed]: {{site.pub-pkg}}/freezed -[inherited-widget-video]: {{site.youtube-site}}/watch?v=og-vJqLzg2c -[managing-state-video]: {{site.youtube-site}}/watch?v=vU9xDLdEZtU -[provider]: {{site.pub-pkg}}/provider -[riverpod]: {{site.pub-pkg}}/riverpod - -## Feedback - -As this section of the website is evolving, -we [welcome your feedback][]! - -[welcome your feedback]: https://google.qualtrics.com/jfe/form/SV_6A9KxXR7XmMrNsy?page="state-management" diff --git a/src/content/get-started/fundamentals/user-input.md b/src/content/get-started/fundamentals/user-input.md deleted file mode 100644 index 067a530ad07..00000000000 --- a/src/content/get-started/fundamentals/user-input.md +++ /dev/null @@ -1,1210 +0,0 @@ ---- -title: Handling user input -description: Learn how to handle user input in Flutter. -prev: - title: State management - path: /get-started/fundamentals/state-management -next: - title: Networking and data - path: /get-started/fundamentals/networking ---- - -Now that you know how to manage state in your -Flutter app, how can you let users interact -with your app and change its state? - -## Introduction to handling user input - -As a multi-platform UI framework, -there are many different ways for users -to interact with a Flutter app. -The resources in this section introduce -you to some of the common widgets used -for enabling user interaction within your app. - -Some user input mechanisms, like [scrolling][], -have already been covered in [Layouts][]. - -:::secondary About design system support -Flutter ships with prebuilt components for two design systems as part of the SDK, -[Material][] and [Cupertino][]. -For educational purposes, this page focuses on Material widgets, components that -are stylized according to the [Material 3 design language][] specifications. - -The Flutter community on [pub.dev][], the package repository for Dart and Flutter, -create and support additional design languages such as [Fluent UI][], [macOS UI][], -and more. If the existing design system components don't quite fit what you need, -Flutter lets you build your own custom widgets, -which is covered at the end of this section. -No matter which design system you choose, the principals on this page apply. -::: - -> **Reference**: -> The [widget catalog][] has an inventory of commonly used widgets in the [Material][] and [Cupertino][] libraries. - -Next, we'll cover a few of the Material widgets that support common -use cases for handling user input in your Flutter app. - -[scrolling]: /get-started/fundamentals/layout#scrolling-widgets -[pub.dev]: {{site.pub}} -[Layouts]: /get-started/fundamentals/layout -[Material]: /ui/widgets/material -[Material 3 design language]: https://m3.material.io/ -[Cupertino]: /ui/widgets/cupertino -[widget catalog]: /ui/widgets#design-systems -[Fluent UI]: {{site.pub}}/packages/fluent_ui -[macOS UI]: {{site.pub}}/packages/macos_ui - -## Buttons - -![A collection of Material 3 Buttons.](/assets/images/docs/fwe/user-input/material-buttons.png) - -Buttons allow a user to initiate an action in the UI by clicking or tapping. -The Material library provides a variety of button types that are functionally similar, -but styled differently for various use cases, including: - -- `ElevatedButton`: A button with some depth. Use elevated buttons to add - dimension to otherwise mostly flat layouts. -- `FilledButton`: A filled button that should be used for - important, final actions that complete a flow, - like **Save**, **Join now**, or **Confirm**. -- `Tonal Button`: A middle ground button between - `FilledButton` and `OutlinedButton`. - They're useful in contexts where a lower-priority button requires more - emphasis than an outline, like **Next**. -- `OutlinedButton`: A button with text and a visible border. - These buttons contain actions that are important, - but aren't the primary action in an app. -- `TextButton`: Clickable text, without a border. - Since text buttons don't have visible borders, - they must rely on their position - relative to other content for context. -- `IconButton`: A button with an icon. -- `FloatingActionButton`: An icon button that hovers over - content to promote a primary action. - -> **Video**: -> [FloatingActionButton (Widget of the Week)][] - -There are usually 3 main aspects to constructing a button: -style, callback, and its child, -as seen in the following `ElevatedButton` sample code: - - -{% comment %} -TODO(khanhnwin): -WidgetStateProperty and styling in the design section of -FWE. Of course, a button's appearance can be dependent on its state. -You can style a button based on its state using `WidgetStateProperty`. -{% endcomment %} - -- A button's callback function, `onPressed`, - determines what happens when the button is clicked, - therefore, this function is where you update your app state. - If the callback is `null`, the button is disabled and - nothing happens when a user presses the button. - -- The button's `child`, which is displayed within the button's content area, - is usually text or an icon that indicates the button's purpose. - -- Finally, a button's `style` controls its appearance: color, border, and so on. - - - - - - ```dart - int count = 0; - - @override - Widget build(BuildContext context) { - return ElevatedButton( - style: ElevatedButton.styleFrom( - textStyle: const TextStyle(fontSize: 20), - ), - onPressed: () { - setState(() { - count += 1; - }); - }, - child: const Text('Enabled'), - ); - } - ``` - - - -> **Checkpoint**: -> Complete this tutorial that teaches you how to build a -> "favorite" button: [Add interactivity to your Flutter app][] - -
    - - **API Docs**: [`ElevatedButton`][] • [`FilledButton`][] • [`OutlinedButton`][] • [`TextButton`][] • [`IconButton`][] • [`FloatingActionButton`][] - -[`ElevatedButton`]: {{site.api}}/flutter/material/ElevatedButton-class.html -[`FilledButton`]: {{site.api}}/flutter/material/FilledButton-class.html -[`FloatingActionButton`]: {{site.api}}/flutter/material/FloatingActionButton-class.html -[FloatingActionButton (Widget of the Week)]: {{site.youtube-site}}/watch/2uaoEDOgk_I?si=MQZcSp24oRaS_kiY -[`IconButton`]: {{site.api}}/flutter/material/IconButton-class.html -[`OutlinedButton`]: {{site.api}}/flutter/material/OutlinedButton-class.html -[`TextButton`]: {{site.api}}/flutter/material/TextButton-class.html -[Add interactivity to your Flutter app]: /ui/interactivity - -## Text - -Several widgets support text input. - -### `SelectableText` - -Flutter's `Text` widget displays text on the screen, -but doesn't allow users to highlight or copy the text. -`SelectableText` displays a string of _user-selectable_ text. - - - - - - ```dart - @override - Widget build(BuildContext context) { - return const SelectableText(''' - Two households, both alike in dignity, - In fair Verona, where we lay our scene, - From ancient grudge break to new mutiny, - Where civil blood makes civil hands unclean. - From forth the fatal loins of these two foes'''); - } - ``` - - - -> **Video**: -> [SelectableText (Widget of the Week)][] - -[SelectableText (Widget of the Week)]: {{site.youtube-site}}/watch?v=ZSU3ZXOs6hc - -### `RichText` - -`RichText` lets you display strings of rich text in your app. -`TextSpan`, similar to `RichText`, allows you to display parts of text with -different text styles. It's not for handling user input, -but is useful if you're allowing users edit and format text. - - - - - - ```dart - @override - Widget build(BuildContext context) { - return RichText( - text: TextSpan( - text: 'Hello ', - style: DefaultTextStyle.of(context).style, - children: const [ - TextSpan(text: 'bold', style: TextStyle(fontWeight: FontWeight.bold)), - TextSpan(text: ' world!'), - ], - ), - ); - } - ``` - - - -> **Video**: -> [Rich Text (Widget of the Week)][] - -> **Code**: -> [Rich Text Editor code][] - -[Rich Text (Widget of the Week)]: {{site.youtube-site}}/watch?v=rykDVh-QFfw -[Rich Text Editor code]: {{site.github}}/flutter/samples/tree/main/simplistic_editor - -### `TextField` - -A `TextField` lets users enter text in text box using a hardware or -onscreen keyboard. - -`TextField`s have many different properties and configurations. -A few of the highlights: - -- `InputDecoration` determines the text field's appearance, - such as color and border. -- `controller`: A `TextEditingController` controls the text being edited. - Why might you need a controller? - By default, your app's users can type - into the text field, but if you want to programmatically control the `TextField` - and clear its value, for example, you'll need a `TextEditingController`. -- `onChanged`: This callback function triggers when the user changes - the text field's value, such as when inserting or removing text. -- `onSubmitted`: This callback is triggered when the user indicates that - they are done editing the text in the field; - for example, by tapping the "enter" key when the text field is in focus. - -The class supports other configurable properties, such as -`obscureText` that turns each letter into a `readOnly` circle as its entered and -`readOnly` which prevents the user from changing the text. - - - - - - ```dart - final TextEditingController _controller = TextEditingController(); - - @override - Widget build(BuildContext context) { - return TextField( - controller: _controller, - decoration: const InputDecoration( - border: OutlineInputBorder(), - labelText: 'Mascot Name', - ), - ); - } - ``` - - - -> **Checkpoint**: -> Complete this 4-part cookbook series that walks -> you through how to create a text field, -> retrieve its value, and update your app state: -> 1. [Create and style a text field][] -> 1. [Retrieve the value of a text field][] -> 1. [Handle changes to a text field][] -> 1. [Focus and text fields][]. - -[Create and style a text field]: /cookbook/forms/text-input -[Retrieve the value of a text field]: /cookbook/forms/retrieve-input -[Handle changes to a text field]: /cookbook/forms/text-field-changes -[Focus and text fields]: /cookbook/forms/focus - -### Form - -`Form` is an optional container for grouping together multiple -form field widgets, such as `TextField`. - -Each individual form field should be wrapped in a `FormField` -widget with the `Form` widget as a common ancestor. -Convenience widgets exist that pre-wrap form field widgets in a -`FormField` for you. -For example, the `Form` widget version of `TextField` is `TextFormField`. - -Using a `Form` provides access to a `FormState`, -which lets you save, reset, and validate each `FormField` -that descends from this `Form`. -You can also provide a `GlobalKey` to identify a specific form, -as shown in the following code: - -```dart -final GlobalKey _formKey = GlobalKey(); - -@override -Widget build(BuildContext context) { - return Form( - key: _formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextFormField( - decoration: const InputDecoration( - hintText: 'Enter your email', - ), - validator: (String? value) { - if (value == null || value.isEmpty) { - return 'Please enter some text'; - } - return null; - }, - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), - child: ElevatedButton( - onPressed: () { - // Validate returns true if the form is valid, or false otherwise. - if (_formKey.currentState!.validate()) { - // Process data. - } - }, - child: const Text('Submit'), - ), - ), - ], - ), - ); -} -``` - -> **Checkpoint**: -> Complete this tutorial to learn how to [build a form with validation][]. - -> **Demo**: -> [Form app][] - -> **Code**: -> [Form app code][] - -
    - - **API Docs**: [`TextField`][] • [`RichText`][] • [`SelectableText`][] • [`Form`][] - -[Build a form with validation]: /cookbook/forms/validation -[Form app]: https://github.com/flutter/samples/tree/main/form_app/ -[Form app code]: {{site.github}}/flutter/samples/tree/main/form_app -[`Form`]: {{site.api}}/flutter/widgets/Form-class.html -[`TextField`]: {{site.api}}/flutter/material/TextField-class.html -[`RichText`]: {{site.api}}/flutter/widgets/RichText-class.html -[`SelectableText`]: {{site.api}}/flutter/material/SelectableText-class.html - -## Select a value from a group of options - -Provide a way to users to select from several options. - -### SegmentedButton - -`SegmentedButton` allows users to select from a -minimal group of 2-5 items. - -The data type, ``, can be a built-in type such as -`int`, `String`, `bool` or an enum. -A `SegmentedButton` has a few relevant properties: - -- `segments`, a list of `ButtonSegment`s, where each represents a "segment" - or option that the user can select. - Visually, each `ButtonSegment` can have an icon, text label, or both. - -- `multiSelectionEnabled` indicates whether the user is allowed - to select multiple options. This property defaults to false. - -- `selected` identifies the currently selected value(s). - **Note:** `selected` is of type of `Set`, so if you're only - allowing users to select one value, that value must be - provided as a`Set` with a single element. - -- The `onSelectionChanged` callback triggers when a user selects any segments. - It provides a list of the selected segments so you can update your app state. - -- Additional styling parameters allow you to modify the button's appearance. - For example, `style` takes a `ButtonStyle`, - providing a way to configure a `selectedIcon`. - - - - - - ```dart - enum Calendar { day, week, month, year } - - // StatefulWidget... - Calendar calendarView = Calendar.day; - - @override - Widget build(BuildContext context) { - return SegmentedButton( - segments: const >[ - ButtonSegment( - value: Calendar.day, - label: Text('Day'), - icon: Icon(Icons.calendar_view_day)), - ButtonSegment( - value: Calendar.week, - label: Text('Week'), - icon: Icon(Icons.calendar_view_week)), - ButtonSegment( - value: Calendar.month, - label: Text('Month'), - icon: Icon(Icons.calendar_view_month)), - ButtonSegment( - value: Calendar.year, - label: Text('Year'), - icon: Icon(Icons.calendar_today)), - ], - selected: {calendarView}, - onSelectionChanged: (Set newSelection) { - setState(() { - // By default, there is only a single segment that can be - // selected at a time, so its value is always the first - calendarView = newSelection.first; - }); - }, - ); - } - ``` - - - -### Chip - -`Chip` is a compact way of representing an -attribute, text, entity, or action for a specific context. -Specialized `Chip` widgets exist for specific use cases: - -- [InputChip][] represents a complex piece of information, - such as an entity (person, place, or thing), or - conversational text, in a compact form. -- [ChoiceChip][] allows a single selection from a set of options. - Choice chips contain related descriptive text or categories. -- [FilterChip][] uses tags or descriptive words to filter content. -- [ActionChip][] represents an action related to primary content. - -Every `Chip` widget requires a `label`. -It can optionally have an `avatar` (such as an icon or a user's profile picture) -and an `onDeleted` callback, which shows a delete icon that -when triggered, deletes the chip. -A `Chip` widget's appearance can also be customized by setting a -number of optional parameters such as `shape`, `color`, and `iconTheme`. - -You will typically use `Wrap`, a widget that displays its children in -multiple horizontal or vertical runs, to make sure your chips wrap and -don't get cut off at the edge of your app. - - - - - - ```dart - @override - Widget build(BuildContext context) { - return const SizedBox( - width: 500, - child: Wrap( - alignment: WrapAlignment.center, - spacing: 8, - runSpacing: 4, - children: [ - Chip( - avatar: CircleAvatar( - backgroundImage: AssetImage('assets/images/dash_chef.png')), - label: Text('Chef Dash'), - ), - Chip( - avatar: CircleAvatar( - backgroundImage: - AssetImage('assets/images/dash_firefighter.png')), - label: Text('Firefighter Dash'), - ), - Chip( - avatar: CircleAvatar( - backgroundImage: AssetImage('assets/images/dash_musician.png')), - label: Text('Musician Dash'), - ), - Chip( - avatar: CircleAvatar( - backgroundImage: AssetImage('assets/images/dash_artist.png')), - label: Text('Artist Dash'), - ), - ], - ), - ); - } - ``` - - - -[InputChip]: {{site.api}}/flutter/material/InputChip-class.html -[ChoiceChip]: {{site.api}}/flutter/material/ChoiceChip-class.html -[FilterChip]: {{site.api}}/flutter/material/FilterChip-class.html -[ActionChip]: {{site.api}}/flutter/material/ActionChip-class.html - - -### `DropdownMenu` - -A `DropdownMenu` allows users to select a choice from a menu -of options and places the selected text into a `TextField`. -It also allows users to filter the menu items based on the text input. - -Configuration parameters include the following: - -- `dropdownMenuEntries` provides a list of `DropdownMenuEntry`s that - describes each menu item. - The menu might contain information such as a text label, and - a leading or trailing icon. - (This is also the only required parameter.) -- `TextEditingController` allows programmatically controlling the `TextField`. -- The `onSelected` callback triggers when the user selects an option. -- `initialSelection` allows you to configure the default value. -- Additional parameters are also available for - customizing the widget's look and behavior. - - - - - - ```dart - enum ColorLabel { - blue('Blue', Colors.blue), - pink('Pink', Colors.pink), - green('Green', Colors.green), - orange('Orange', Colors.orange), - grey('Grey', Colors.grey); - - const ColorLabel(this.label, this.color); - final String label; - final Color color; - } - - // StatefulWidget... - @override - Widget build(BuildContext context) { - return DropdownMenu( - initialSelection: ColorLabel.green, - controller: colorController, - // requestFocusOnTap is enabled/disabled by platforms when it is null. - // On mobile platforms, this is false by default. Setting this to true will - // trigger focus request on the text field and virtual keyboard will appear - // afterward. On desktop platforms however, this defaults to true. - requestFocusOnTap: true, - label: const Text('Color'), - onSelected: (ColorLabel? color) { - setState(() { - selectedColor = color; - }); - }, - dropdownMenuEntries: ColorLabel.values - .map>( - (ColorLabel color) { - return DropdownMenuEntry( - value: color, - label: color.label, - enabled: color.label != 'Grey', - style: MenuItemButton.styleFrom( - foregroundColor: color.color, - ), - ); - }).toList(), - ); - } - ``` - - - -> **Video**: -> [DropdownMenu (Widget of the Week)][] - -[DropdownMenu (Widget of the Week)]: {{site.youtube-site}}/watch?v=giV9AbM2gd8?si=E23hjg72cjMTe_mz - -### Slider - -The `Slider` widget lets a user adjust a value by moving an indicator, -such as a volume bar. - -Configuration parameters for the `Slider` widget: - -- `value` represents the slider's current value -- `onChanged` is the callback that gets triggered when the handle is moved -- `min` and `max` establish minimum and maximum values allowed by the slider -- `divisions` establishes a discrete interval with which the user can move the - handle along the track. - - - - - - ```dart - double _currentVolume = 1; - - @override - Widget build(BuildContext context) { - return Slider( - value: _currentVolume, - max: 5, - divisions: 5, - label: _currentVolume.toString(), - onChanged: (double value) { - setState(() { - _currentVolume = value; - }); - }, - ); - } - ``` - - - -> **Video**: -> [Slider, RangeSlider, CupertinoSlider (Widget of the Week)][] - -
    - - **API Docs:** [`SegmentedButton`][] • [`DropdownMenu`][] • [`Slider`][] • [`Chip`][] - -[Slider, RangeSlider, CupertinoSlider (Widget of the Week)]: {{site.youtube-site}}/watch?v=ufb4gIPDmEss -[`SegmentedButton`]: {{site.api}}/flutter/material/SegmentedButton-class.html -[`DropdownMenu`]: {{site.api}}/flutter/material/DropdownMenu-class.html -[`Slider`]: {{site.api}}/flutter/material/Slider-class.html -[`Chip`]: {{site.api}}/flutter/material/Chip-class.html - -## Toggle between values - -There are several ways that your UI can allow toggling between values. - -### Checkbox, Switch, and Radio - -Provide an option to toggle a single value on and off. -The functional logic behind these widgets are the same, -as all 3 are built on top of `ToggleableStateMixin`, though -each provides slight presentation differences.: - -- `Checkbox` is a container that is empty when false or - filled with a checkmark when true. -- `Switch` has a handle that is on the left when false and - slides to the right when true. -- `Radio` is similar to a `Checkbox` in that it's a container that is - empty when false, but filled in when true. - -The configuration for `Checkbox` and `Switch` contain: - -- a `value` that is `true` or `false` -- and an `onChanged` callback which is triggered when - the user toggles the widget - -### Checkbox - - - - - - ```dart - bool isChecked = false; - - @override - Widget build(BuildContext context) { - return Checkbox( - checkColor: Colors.white, - value: isChecked, - onChanged: (bool? value) { - setState(() { - isChecked = value!; - }); - }, - ); - } - ``` - - - -### Switch - - - - - - ```dart - bool light = true; - - @override - Widget build(BuildContext context) { - return Switch( - // This bool value toggles the switch. - value: light, - activeThumbColor: Colors.red, - onChanged: (bool value) { - // This is called when the user toggles the switch. - setState(() { - light = value; - }); - }, - ); - } - ``` - - - -### Radio - -A `RadioGroup` contains `Radio` buttons that allow the user to -select between mutually exclusive values. -When the user selects a radio button in a group, -the other radio buttons are unselected. - -- A particular `Radio` button's `value` represent that button's value. -- The selected value for a `RadioGroup` is identified by - the `groupValue` parameter. -- `RadioGroup` has an `onChanged` callback that - gets triggered when users click it, like `Switch` and `Checkbox`. - - - - - - ```dart - enum Character { musician, chef, firefighter, artist } - - class RadioExample extends StatefulWidget { - const RadioExample({super.key}); - - @override - State createState() => _RadioExampleState(); - } - - class _RadioExampleState extends State { - Character? _character = Character.musician; - - void setCharacter(Character? value) { - setState(() { - _character = value; - }); - } - - @override - Widget build(BuildContext context) { - return RadioGroup( - groupValue: _character, - onChanged: setCharacter, - child: Column( - children: [ - ListTile( - title: const Text('Musician'), - leading: Radio(value: Character.musician), - ), - ListTile( - title: const Text('Chef'), - leading: Radio(value: Character.chef), - ), - ListTile( - title: const Text('Firefighter'), - leading: Radio(value: Character.firefighter), - ), - ListTile( - title: const Text('Artist'), - leading: Radio(value: Character.artist), - ), - ], - ), - ); - } - } - ``` - - - -#### Bonus: CheckboxListTile & SwitchListTile - -These convenience widgets are the same checkbox and switch widgets, -but support a label (as a `ListTile`). - - - - - - ```dart - double timeDilation = 1.0; - bool _lights = false; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - CheckboxListTile( - title: const Text('Animate Slowly'), - value: timeDilation != 1.0, - onChanged: (bool? value) { - setState(() { - timeDilation = value! ? 10.0 : 1.0; - }); - }, - secondary: const Icon(Icons.hourglass_empty), - ), - SwitchListTile( - title: const Text('Lights'), - value: _lights, - onChanged: (bool value) { - setState(() { - _lights = value; - }); - }, - secondary: const Icon(Icons.lightbulb_outline), - ), - ], - ); - } - ``` - - - -> **Video**: -> [CheckboxListTile (Widget of the Week)][] - -> **Video**: -> [SwitchListTile (Widget of the Week)][] - -
    - - **API Docs**: -[`Checkbox`][] • [`CheckboxListTile`][] • [`Switch`][] • [`SwitchListTile`][] • -[`Radio`][] - -[CheckboxListTile (Widget of the Week)]: {{site.youtube-site}}/watch?v=RkSqPAn9szs -[SwitchListTile (Widget of the Week)]: {{site.youtube-site}}/watch?v=0igIjvtEWNU - -[`Checkbox`]: {{site.api}}/flutter/material/Checkbox-class.html -[`CheckboxListTile`]: {{site.api}}/flutter/material/CheckboxListTile-class.html -[`Switch`]: {{site.api}}/flutter/material/Switch-class.html -[`SwitchListTile`]: {{site.api}}/flutter/material/SwitchListTile-class.html -[`Radio`]: {{site.api}}/flutter/material/Radio-class.html - -## Select a date or time - -Widgets are provided so the user can select a date and time. - -There is a set of dialogs that enable users to select a date or time, -as you'll see in the following sections. -With the exception of differing date types - -`DateTime` for dates vs `TimeOfDay` for time - -these dialogs function similarly, you can configure them by providing: - -- a default `initialDate` or `initialTime` -- or an `initialEntryMode` that determines the picker UI that's displayed. - -### DatePickerDialog - -This dialog allows the user to select a date or a range of dates. -Activate by calling the `showDatePicker` function, -which returns a `Future`, -so don't forget to await the asynchronous function call! - - - - - - ```dart - DateTime? selectedDate; - - @override - Widget build(BuildContext context) { - var date = selectedDate; - - return Column(children: [ - Text( - date == null - ? 'You haven\\\'t picked a date yet.' - : DateFormat('MM-dd-yyyy').format(date), - ), - ElevatedButton.icon( - icon: const Icon(Icons.calendar_today), - onPressed: () async { - var pickedDate = await showDatePicker( - context: context, - initialEntryMode: DatePickerEntryMode.calendarOnly, - initialDate: DateTime.now(), - firstDate: DateTime(2019), - lastDate: DateTime(2050), - ); - - setState(() { - selectedDate = pickedDate; - }); - }, - label: const Text('Pick a date'), - ) - ]); - } - ``` - - - -### TimePickerDialog - -`TimePickerDialog` is a dialog that presents a time picker. -It can be activated by calling the `showTimePicker()` function. -Instead of returning a `Future`, -`showTimePicker` instead returns a `Future`. -Once again, don't forget to await the function call! - - - - - - ```dart - TimeOfDay? selectedTime; - - @override - Widget build(BuildContext context) { - var time = selectedTime; - - return Column(children: [ - Text( - time == null ? 'You haven\\\'t picked a time yet.' : time.format(context), - ), - ElevatedButton.icon( - icon: const Icon(Icons.calendar_today), - onPressed: () async { - var pickedTime = await showTimePicker( - context: context, - initialEntryMode: TimePickerEntryMode.dial, - initialTime: TimeOfDay.now(), - ); - - setState(() { - selectedTime = pickedTime; - }); - }, - label: const Text('Pick a time'), - ) - ]); - } - ``` - - - -:::tip -Calling `showDatePicker()` and `showTimePicker()` -is equivalent to calling `showDialog()` with `DatePickerDialog()` and -`TimePickerDialog()`, respectively. -Internally, both functions use the `showDialog()` function with -their respective `Dialog` widgets. -To enable state restoration, you can also push -`DatePickerDialog()` and `TimePickerDialog()` directly -on to the `Navigator` stack. -::: - -
    - - **API Docs:** -[`showDatePicker`][] • [`showTimePicker`][] - -[`showDatePicker`]: {{site.api}}/flutter/material/showDatePicker.html -[`showTimePicker`]: {{site.api}}/flutter/material/showTimePicker.html - -## Swipe & slide - -### [`Dismissible`][] - -A `Dismissible` is a widget that enables users to dismiss it by swiping. -It has a number of configuration parameters, including: - -- A `child` widget -- An `onDismissed` callback that is triggered when the user swipes -- Styling parameters such as `background` -- It's important to include a `key` object as well so that they can be uniquely - identified from sibling `Dismissible` widgets in the widget tree. - - - - - - ```dart - List items = List.generate(100, (int index) => index); - - @override - Widget build(BuildContext context) { - return ListView.builder( - itemCount: items.length, - padding: const EdgeInsets.symmetric(vertical: 16), - itemBuilder: (BuildContext context, int index) { - return Dismissible( - background: Container( - color: Colors.green, - ), - key: ValueKey(items[index]), - onDismissed: (DismissDirection direction) { - setState(() { - items.removeAt(index); - }); - }, - child: ListTile( - title: Text( - 'Item ${items[index]}', - ), - ), - ); - }, - ); - } - ``` - - - -> **Video**: -> [Dismissible (Widget of the Week)][] - -> **Checkpoint**: -> Complete this tutorial on how to [implement swipe to dismiss][] using the -> dismissible widget. - -
    - - **API Docs:** -[`Dismissible`][] - -[Dismissible (Widget of the Week)]: {{site.youtube-site}}/watch?v=iEMgjrfuc58?si=f0S7IdaA9PIWIYvl -[Implement swipe to dismiss]: /cookbook/gestures/dismissible -[`Dismissible`]: {{site.api}}/flutter/widgets/Dismissible-class.html - -## Looking for more widgets? - -This page features just a few of the common Material widgets that -you can use for handling user input in your Flutter app. -Check out the [Material Widget library][] and -[Material Library API docs][] for a full list of widgets. - -> **Demo**: -> See Flutter's [Material 3 Demo][] for a curated sample of user input widgets -> available in the Material library. - -If the Material and Cupertino libraries don't have a widget that -does what you need, check out [pub.dev][] to find -Flutter & Dart community-owned and maintained packages. -For example, the [`flutter_slidable`][] package provides -a `Slidable` widget that is more customizable than -the `Dismissible` widget described in the previous section. - -> **Video**: -> [flutter_slidable (Package of the Week)][] - -[Material Widget Library]: /ui/widgets/material -[Material Library API docs]: {{site.api}}/flutter/material/material-library.html -[Material 3 Demo]: https://github.com/flutter/samples/tree/main/material_3_demo - -[`flutter_slidable`]: {{site.pub}}/packages/flutter_slidable -[flutter_slidable (Package of the Week)]: {{site.youtube-site}}/watch?v=QFcFEpFmNJ8 - -## Build interactive widgets with GestureDetector - -Have you scoured the widget libraries, pub.dev, asked your coding friends, -and still can't find a widget that -fits the user interaction that you're looking for? -You can build your own custom widget and -make it interactive using `GestureDetector`. - -> **Checkpoint**: -> Use this recipe as a starting point to create your own _custom_ button widget -> that can [handle taps][]. - -> **Video**: -> [GestureDetector (Widget of the Week)][] - -> **Reference**: -> Check out [Taps, drags, and other gestures][] which explains how to listen -> for, and respond to, gestures in Flutter. - -> **Bonus Video**: -> Curious how Flutter's `GestureArena` turns raw user interaction data into -> human recognizable concepts like taps, drags, and pinches? -> Check out this video: [GestureArena (Decoding Flutter)][] - -[handle taps]: /cookbook/gestures/handling-taps -[GestureDetector (Widget of the Week)]: {{site.youtube-site}}/watch?v=WhVXkCFPmK4 -[Taps, drags, and other gestures]: /ui/interactivity/gestures#gestures -[GestureArena (Decoding Flutter)]: {{site.youtube-site}}/watch?v=Q85LBtBdi0U - -### Don't forget about accessibility! - -If you're building a custom widget, -annotate its meaning with the `Semantics` widget. -It provides descriptions and metadata to screen readers and -other semantic analysis-based tools. - -> **Video**: -> [Semantics (Flutter Widget of the Week)][] - - -
    - - **API Docs**: -[`GestureDetector`][] • [`Semantics`][] - -[`GestureDetector`]: {{site.api}}/flutter/widgets/GestureDetector-class.html -[`Semantics`]: {{site.api}}/flutter/widgets/Semantics-class.html - -## Testing - -Once you have finished building user interactions -into your app, don't forget to write tests to -ensure that everything works as expected! - -These tutorials walk you through writing tests that -simulate user interactions in your app: - -> **Checkpoint**: -> Follow this [tap, drag, and enter text][] cookbook article and learn how to -> use `WidgetTester` to simulate and test user interactions in your app. - -> **Bonus Tutorial**: -> The [handle scrolling][] cookbook recipe shows you how to verify that -> lists of widgets contain the expected content by -> scrolling through the lists using widget tests. - -[Semantics (Flutter Widget of the Week)]: {{site.youtube-site}}/watch?v=NvtMt_DtFrQ?si=o79BqAg9NAl8EE8_ -[Tap, drag, and enter text]: /cookbook/testing/widget/tap-drag -[Handle scrolling]: /cookbook/testing/widget/scrolling - -## Next: Networking - -This page was an introduction to handling user input. -Now that you know how to handle input from app users, -you can make your app even more interesting by adding -external data. In the next section, -you'll learn how to fetch data for your app over a network, -how to convert data to and from JSON, authentication, -and other networking features. - -## Feedback - -As this section of the website is evolving, -we [welcome your feedback][]! - -[welcome your feedback]: https://google.qualtrics.com/jfe/form/SV_6A9KxXR7XmMrNsy?page="user-input" diff --git a/src/content/get-started/fundamentals/widgets.md b/src/content/get-started/fundamentals/widgets.md deleted file mode 100644 index a699a0d2592..00000000000 --- a/src/content/get-started/fundamentals/widgets.md +++ /dev/null @@ -1,294 +0,0 @@ ---- -title: Widgets -description: Learn the basic building blocks of Flutter. -prev: - title: Intro to Dart - path: /get-started/fundamentals/dart -next: - title: Layout - path: /get-started/fundamentals/layout ---- - -To get started with Flutter, -you need to have some familiarity with the -Dart programming language, which Flutter -applications are written in, and widgets, -which are the building blocks of Flutter UI. -Both will be introduced on this page, but you'll continue -learning about each throughout this series. -Additional resources are listed throughout this page, -but you do not need to be an expert in either -subject in order to continue. - -## Widgets - -In regard to Flutter, you'll often hear -"everything is a widget". -Widgets are the building blocks of a -Flutter app's user interface, -and each widget is an immutable declaration of part -of the user interface. Widgets are used -to describe all aspects of a user interface, -including physical aspects such as text and buttons to -lay out effects like padding and alignment. - -Widgets form a hierarchy based on composition. -Each widget nests inside its parent and -can receive context from the parent. -This structure carries all the way up to the root -widget, as this trivial example shows: - -```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 MaterialApp( // Root widget - 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'), - ), - ], - ); - }, - ), - ), - ), - ); - } -} -``` - -In the preceding code, -all instantiated classes are widgets: -`MaterialApp`, `Scaffold`, `AppBar`, `Text`, -`Center`, `Builder`, `Column`, `SizedBox`, and -`ElevatedButton`. - -### Widget composition - -As mentioned, Flutter emphasizes widgets as a unit -of composition. Widgets are typically composed of -many other small, single-purpose widgets that -combine to produce powerful effects. - -There are layout widgets such -as `Padding`, `Alignment`, `Row`, `Column`, -and `Grid`. These layout widgets do not have a -visual representation of their own. -Instead, their sole purpose is to -control some aspect of another widget's layout. -Flutter also includes utility widgets that -take advantage of this compositional approach. -For example, `Container`, a commonly used widget, -is made up of several widgets responsible for layout, -painting, positioning, and sizing. -Some widgets have visual representation, -such as `ElevatedButton` and -`Text` in the preceding example, as well as -widgets like `Icon` and `Image`. - -If you run the code from the preceding example, -Flutter paints a button with the text -"Hello, World!" centered on the screen, laid out vertically. -To position these elements, there's a `Center` widget, -which positions its children in the center -of the available space, and a `Column` widget, -which lays out its children vertically one after another. - -A diagram that shows widget composition with a series of lines and nodes. - - -In the [next page][] in this series, you will -learn more about layout in Flutter. - -### Building widgets - -To create a user interface in Flutter, -you override the [`build`][] method on widget objects. -All widgets must have a build method, -and it must return another widget. For example, -if you want to add text to the screen with some padding, -you could write it like this: - -```dart -class PaddedText extends StatelessWidget { - const PaddedText({super.key}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: const Text('Hello, World!'), - ); - } -} -``` - -The framework calls the `build` method when this -widget is created and when the dependencies of this -widget change (such as state that is passed into the widget). -This method can potentially be called in every frame (typically 60 times a second) -and should not have any side effects beyond -building a widget. -To learn more about how Flutter renders widgets, -check out the [Flutter architectural overview][]. - -### Widget state - -The framework introduces two major classes of widget: -stateful and stateless widgets. - -Widgets that have no mutable state -(they have no class properties -that change over time) subclass [`StatelessWidget`][]. -Many built-in widgets are stateless, -such as `Padding`, `Text`, and `Icon`. -When you create your own widgets, -you'll create `Stateless` widgets most of the time. - -On the other hand, -if the unique characteristics of a widget need to change -based on user interaction or other factors, -that widget is stateful. -For example, if a widget has a counter that -increments whenever the user taps a button, -then the value of the counter is the state for that widget. -When that value changes, the widget needs to be -rebuilt to update its part of the UI. -These widgets subclass [`StatefulWidget`][], -and (because the widget itself is immutable) -they store mutable state in a separate class that -subclasses [`State`][]. -`StatefulWidgets` don't have a `build` method; -instead, their user interface is built through -their `State` object, as shown in the example below. - -```dart -class CounterWidget extends StatefulWidget { - @override - State createState() => _CounterWidgetState(); -} - -class _CounterWidgetState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - return Text('$_counter'); - } -} -``` - -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`'s `build` method again. - -Separating state from widget objects -lets other widgets treat both -stateless and stateful widgets in exactly the same way, -without being concerned about losing 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 -the child's persistent state. -The framework does all the work of finding and -reusing existing state objects when appropriate. - -There's more information about -[`StatefulWidget`][] objects later in this -series, in the [state management lesson][]. - -## Important widgets to know - -The Flutter SDK includes many built-in widgets, -from the smallest pieces of UI, like `Text`, -to layout widgets, and widgets that style -your application. The following widgets are -the most important to be aware of as you move onto the -next lesson in the learning pathway. - -* [`Container`][] -* [`Text`][] -* [`Scaffold`][] -* [`AppBar`][] -* [`Row`][] and [`Column`][] -* [`ElevatedButton`][] -* [`Image`][] -* [`Icon`][] - -## Widget previewer - -You can see your widgets render in real-time, separate from -a full app. To learn more, see the -[Flutter Widget Previewer][] guide. - -[Flutter Widget Previewer]: /tools/widget-previewer - -## Next: Layouts - -This page is an introduction to foundational -Flutter concepts, like widgets, -and helps you become familiar with reading -Flutter and Dart code. It's okay if you don't -feel clear on every topic you encountered, as every page after -this is a deep-dive on specific topics. -In the next section, you'll start building more -interesting UIs by creating more complex layouts in Flutter. - -If you'd like practice with the -information you learned on this page, -you can read [Building user interfaces with Flutter][]. - -[Building user interfaces with Flutter]: /ui -[`build`]: {{site.api}}/flutter/widgets/StatelessWidget/build.html -[next page]: /get-started/fundamentals/layout -[Flutter architectural overview]: /resources/architectural-overview -[`StatelessWidget`]: {{site.api}}/flutter/widgets/StatelessWidget-class.html -[`StatefulWidget`]: {{site.api}}/flutter/widgets/StatefulWidget-class.html -[`State`]: {{site.api}}/flutter/widgets/State-class.html -[`setState`]: {{site.api}}/flutter/widgets/State/setState.html -[state management lesson]: /get-started/fundamentals/state-management -[`AppBar`]: {{site.api}}/flutter/material/AppBar-class.html -[`Column`]: {{site.api}}/flutter/widgets/Column-class.html -[`Container`]: {{site.api}}/flutter/widgets/Container-class.html -[`ElevatedButton`]: {{site.api}}/flutter/material/ElevatedButton-class.html -[`Icon`]: {{site.api}}/flutter/widgets/Icon-class.html -[`Image`]: {{site.api}}/flutter/widgets/Image-class.html -[`Row`]: {{site.api}}/flutter/widgets/Row-class.html -[`Scaffold`]: {{site.api}}/flutter/material/Scaffold-class.html -[`Text`]: {{site.api}}/flutter/widgets/Text-class.html - -## Feedback - -As this section of the website is evolving, -we [welcome your feedback][]! - -[welcome your feedback]: https://google.qualtrics.com/jfe/form/SV_6A9KxXR7XmMrNsy?page="widgets" diff --git a/src/content/get-started/index.md b/src/content/get-started/index.md deleted file mode 100644 index 41853018a0e..00000000000 --- a/src/content/get-started/index.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Get started with Flutter -shortTitle: Get started -description: >- - Get started developing your first app with Flutter! -toc: false -showBanner: false ---- - -Ready to build beautiful, multiplatform apps from a single codebase? -Choose from the following two options to get started setting up -your Flutter development environment. - - - -{% render "docs/china-notice.md" %} diff --git a/src/content/get-started/learn-flutter.md b/src/content/get-started/learn-flutter.md deleted file mode 100644 index c93b2cceec1..00000000000 --- a/src/content/get-started/learn-flutter.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: Learn Flutter -description: Resources to help you learn Flutter. -showToc: false ---- - - - -## For new Flutter developers - -If you're brand new to Flutter, -we suggest you complete the following resources -in order: - - 1. [Dart language overview][] - Flutter uses the Dart language. - If you have experience with other - object-oriented languages, like Java, C++, or - Swift, Dart should feel familiar to you, - and you might be comfortable skipping this section. - 2. [Write your first Flutter app][] - This codelab introduces the basics of Flutter - by creating an app that works on mobile, - desktop, and web. - 3. [Learn the fundamentals][] - This is an opinionated set of documentation for - the new Flutter developer. It guides you - through some of the most important pieces of - building Flutter applications. - -## Apply your existing knowledge - -* [Flutter for Android developers][] -* [Flutter for SwiftUI developers][] -* [Flutter for UIKit developers][] -* [Flutter for React Native developers][] -* [Flutter for web developers][] -* [Flutter for Xamarin.Forms developers][] - -## Other resources - -* [Flutter samples][] -* [Flutter learning resources][] -* [Flutter API Docs][] - -Reach out to us on our [mailing list][]. -We'd love to hear from you! - -Happy Fluttering! - -[Dart language overview]: {{site.dart-site}}/overview -[Flutter API Docs]: {{site.api}} -[Flutter learning resources]: /reference/learning-resources -[Flutter for Android developers]: /get-started/flutter-for/android-devs -[Flutter for SwiftUI developers]: /get-started/flutter-for/swiftui-devs -[Flutter for UIKit developers]: /get-started/flutter-for/uikit-devs -[Flutter for React Native developers]: /get-started/flutter-for/react-native-devs -[Flutter samples]: https://github.com/flutter/samples -[Flutter for web developers]: /get-started/flutter-for/web-devs -[Flutter for Xamarin.Forms developers]: /get-started/flutter-for/xamarin-forms-devs -[Learn the fundamentals]: /get-started/fundamentals -[mailing list]: mailto:{{site.email}} -[Write your first Flutter app]: /get-started/codelab diff --git a/src/content/get-started/custom.md b/src/content/install/custom.md similarity index 100% rename from src/content/get-started/custom.md rename to src/content/install/custom.md diff --git a/src/content/install/index.md b/src/content/install/index.md index 68c4bca060f..7dc84cbfb5d 100644 --- a/src/content/install/index.md +++ b/src/content/install/index.md @@ -5,84 +5,68 @@ description: >- Learn how to install the Flutter SDK. --- -Learn how to install the Flutter SDK and -manage the resulting installation. +Ready to build beautiful, multiplatform apps from a single codebase? +Choose from the following two options to get started setting up +your Flutter development environment. -:::recommend -If you've never set up or developed an app with Flutter before -and plan to use VS Code or another Code OSS-based editor, -follow [Set up and test drive Flutter][] instead. -::: - -[Set up and test drive Flutter]: /get-started/quick - -## Try Flutter {:#try} - -You can quickly try Flutter online without any local setup. - -
    - + - - -## Install Flutter {: #install} +## Try Flutter {:#try} -To get started developing with Flutter, -follow these steps to install and set up Flutter -for your preferred development environment and target devices. +You can quickly try Flutter online without any local setup. -:::tip -If you've never set up or developed an app with Flutter before -and plan to use VS Code or another Code - OSS derived editor, -consider following the [Flutter quick start][] instead. -::: - -[Flutter quick start]: /get-started/quick + ## Update Flutter {: #update} @@ -107,3 +91,34 @@ up to date with the **latest** `stable` or `beta` releases. ::: [SDK archive]: /install/archive + +## Troubleshoot and uninstall Flutter {: #uninstall} + +Guides to help you resolve common issues with your Flutter development environment and uninstall Flutter. + + \ No newline at end of file diff --git a/src/content/install/manual.md b/src/content/install/manual.md index 65588f811cd..5f5cbc06d1a 100644 --- a/src/content/install/manual.md +++ b/src/content/install/manual.md @@ -365,7 +365,7 @@ the Flutter team recommends you first try out Write your first app
  • - Learn the fundamentals + Learn the fundamentals
  • Explore Flutter widgets diff --git a/src/content/get-started/quick.md b/src/content/install/quick.md similarity index 100% rename from src/content/get-started/quick.md rename to src/content/install/quick.md diff --git a/src/content/install/with-vs-code.md b/src/content/install/with-vs-code.md index 2cd59d11cc7..497a25a4a83 100644 --- a/src/content/install/with-vs-code.md +++ b/src/content/install/with-vs-code.md @@ -282,7 +282,7 @@ the Flutter team recommends you first try out Write your first app
  • - Learn the fundamentals + Learn the fundamentals
  • Explore Flutter widgets diff --git a/src/content/learn/tutorial/http-requests.md b/src/content/learn/tutorial/http-requests.md index 84cbbc17a20..802fc68449e 100644 --- a/src/content/learn/tutorial/http-requests.md +++ b/src/content/learn/tutorial/http-requests.md @@ -35,9 +35,6 @@ The core tenet of MVVM (and many other patterns) is *separation of concerns*. Managing state in separate classes (outside your UI widgets) makes your code more testable, reusable, and easier to maintain. - - A single feature in your app contains each one of the MVVM components. In this tutorial, in addition to Flutter widgets, you'll create `ArticleModel`, `ArticleViewModel`, and `ArticleView`. diff --git a/src/content/learn/tutorial/layout.md b/src/content/learn/tutorial/layout.md index 7966750721e..000c802698a 100644 --- a/src/content/learn/tutorial/layout.md +++ b/src/content/learn/tutorial/layout.md @@ -229,7 +229,7 @@ providing a declarative alternative to the following: In this case, it adds five `Row` widgets to the column, one for each guess on the `Game` object. -[collection for element]: /language/collections#for-element +[collection for element]: {{site.dart-site}}/language/collections#for-element #### An updated widget tree diff --git a/src/content/llms.txt b/src/content/llms.txt index a04cded36fa..1c709ebb4ad 100644 --- a/src/content/llms.txt +++ b/src/content/llms.txt @@ -10,7 +10,7 @@ This file provides a curated list of resources to help Large Language Models und - [Installation guide](https://docs.flutter.dev/get-started/install): The first step for any developer to set up their environment for Flutter development. - [Introduction to declarative UI](https://docs.flutter.dev/get-started/flutter-for/declarative): This introduction describes the conceptual difference between the declarative style used by Flutter, and the imperative style used by many other UI frameworks. - [Write Your First App Codelab](https://codelabs.developers.google.com/codelabs/flutter-codelab-first): The canonical hands-on tutorial for building a basic Flutter application. -- [Flutter Fundamentals](https://docs.flutter.dev/get-started/fundamentals): A guide for new developers learning Flutter, covering fundamental topics like Dart, widgets, layout, state +- [Flutter Learning Pathway](https://docs.flutter.dev/learn/pathway): A guide for new developers learning Flutter, covering fundamental topics like Dart, widgets, layout, state management, user input, networking, and local data caching. - [Dart language tour](https://dart.dev/language): A comprehensive introduction to the Dart programming language, which is required for Flutter development. - [Flutter Widget of the Week playlist](https://www.youtube.com/watch?v=D0xwcz2IqAY&list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG): Short, engaging videos introducing individual widgets. diff --git a/src/content/platform-integration/android/setup.md b/src/content/platform-integration/android/setup.md index 5045f26946b..14f7b30482e 100644 --- a/src/content/platform-integration/android/setup.md +++ b/src/content/platform-integration/android/setup.md @@ -354,7 +354,7 @@ or begin improving integration with Android. Write your first app
  • - Learn the fundamentals + Learn the fundamentals
  • Explore Flutter widgets diff --git a/src/content/platform-integration/ios/setup.md b/src/content/platform-integration/ios/setup.md index c09e9f2d613..98284208dcc 100644 --- a/src/content/platform-integration/ios/setup.md +++ b/src/content/platform-integration/ios/setup.md @@ -250,7 +250,7 @@ or begin improving integration with iOS. Write your first app
  • - Learn the fundamentals + Learn the fundamentals
  • Explore Flutter widgets diff --git a/src/content/platform-integration/linux/setup.md b/src/content/platform-integration/linux/setup.md index 48881c89206..2089ca8b4dc 100644 --- a/src/content/platform-integration/linux/setup.md +++ b/src/content/platform-integration/linux/setup.md @@ -104,7 +104,7 @@ or begin expanding integration with Linux. Write your first app
  • - Learn the fundamentals + Learn the fundamentals
  • Explore Flutter widgets diff --git a/src/content/platform-integration/macos/setup.md b/src/content/platform-integration/macos/setup.md index 5bc2af471ce..84dae9785be 100644 --- a/src/content/platform-integration/macos/setup.md +++ b/src/content/platform-integration/macos/setup.md @@ -148,7 +148,7 @@ or begin expanding integration with macOS. Write your first app
  • - Learn the fundamentals + Learn the fundamentals
  • Explore Flutter widgets diff --git a/src/content/platform-integration/web/setup.md b/src/content/platform-integration/web/setup.md index c2ac2c19f55..5a649242ba6 100644 --- a/src/content/platform-integration/web/setup.md +++ b/src/content/platform-integration/web/setup.md @@ -81,7 +81,7 @@ or begin expanding integration with the web. Write your first app
  • - Learn the fundamentals + Learn the fundamentals
  • Explore Flutter widgets diff --git a/src/content/platform-integration/windows/setup.md b/src/content/platform-integration/windows/setup.md index 4fb293769e6..7c4e94b2bb8 100644 --- a/src/content/platform-integration/windows/setup.md +++ b/src/content/platform-integration/windows/setup.md @@ -124,7 +124,7 @@ or begin expanding integration with Windows. Write your first app
  • - Learn the fundamentals + Learn the fundamentals
  • Explore Flutter widgets diff --git a/src/content/release/archive-whats-new.md b/src/content/release/archive-whats-new.md index f7a86f64dc3..8d35b409ed4 100644 --- a/src/content/release/archive-whats-new.md +++ b/src/content/release/archive-whats-new.md @@ -192,11 +192,11 @@ This website release includes several important updates! [deep-linking-tool]: /tools/devtools/deep-links [design patterns]: /app-architecture/design-patterns [Flutter AI Toolkit]: /ai/ai-toolkit -[fwe]: /get-started/fundamentals +[fwe]: /learn/pathway [Interactive Media Ads]: {{site.pub-pkg}}/interactive_media_ads [jc]: /platform-integration/android/compose-activity [jetpack-api]: /platform-integration/android/call-jetpack-apis -[Intro to Dart]: /get-started/fundamentals/dart +[Intro to Dart]: {{site.dart-site}}/tutorials [plugin-authors]: /packages-and-plugins/swift-package-manager/for-plugin-authors [Support for WebAssembly (Wasm)]: /platform-integration/web/wasm [web renderers]: /platform-integration/web/renderers @@ -259,12 +259,12 @@ This website release includes several important updates! [DevTools 2.37.2]: /tools/devtools/release-notes/release-notes-2.37.2 [Embedding Flutter on the web]: /platform-integration/web/embedding-flutter-web [Embedding web content into a Flutter app]: /platform-integration/web/web-content-in-flutter -[Flutter fundamentals docs]: /get-started/fundamentals -[Widgets]: /get-started/fundamentals/widgets +[Flutter fundamentals docs]: /learn/pathway +[Widgets]: /learn/tutorial/widget-fundamentals [iOS app extension]: /platform-integration/ios/app-extensions [iOS plugins]: /packages-and-plugins/swift-package-manager/for-plugin-authors [iOS apps]: /packages-and-plugins/swift-package-manager/for-app-developers -[Layout]: /get-started/fundamentals/layout +[Layout]: /learn/tutorial/layout [predictive back gesture]: /platform-integration/android/predictive-back [Tinted app icons]: /deployment/ios#add-an-app-icon @@ -336,7 +336,7 @@ To learn more, check out [dart.dev/go/macros][]. [Adaptive and Responsive design]: /ui/adaptive-responsive [Casual Games Toolkit]: /resources/games-toolkit -[Flutter fundamentals docs]: /get-started/fundamentals +[Flutter fundamentals docs]: /learn/pathway [Flutter install]: /get-started [Flutter web app initialization]: /platform-integration/web/initialization [website README]: {{site.github}}/flutter/website/?tab=readme-ov-file#flutter-documentation-website diff --git a/src/content/tools/property-editor.md b/src/content/tools/property-editor.md index 35dcd8a7a55..620fa2c66c7 100644 --- a/src/content/tools/property-editor.md +++ b/src/content/tools/property-editor.md @@ -48,7 +48,7 @@ UI development and iteration. [VS Code]: /tools/vs-code#property-editor [Android Studio/IntelliJ]: /tools/android-studio#property-editor -[widget constructor invocation]: /get-started/fundamentals/widgets +[widget constructor invocation]: /learn/tutorial/widget-fundamentals ### Runtime usage diff --git a/src/data/glossary.yml b/src/data/glossary.yml index acd6aeca45e..c87df527633 100644 --- a/src/data/glossary.yml +++ b/src/data/glossary.yml @@ -232,8 +232,8 @@ why other state management solutions (like `InheritedWidget` or `Provider`) are often used. related_links: - - text: "State management: Sharing state" - link: "/get-started/fundamentals/state-management#sharing-state-between-widgets" + - text: "State management introduction" + link: "/data-and-backend/state-mgmt/intro" type: "doc" labels: - "state management" @@ -325,8 +325,8 @@ [`StatefulWidget`]: https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html [state]: https://api.flutter.dev/flutter/widgets/State-class.html related_links: - - text: "Fundamentals: Widgets" - link: "/get-started/fundamentals/widgets" + - text: "Widget fundamentals" + link: "/learn/tutorial/widget-fundamentals" type: "doc" - text: "Widget catalog" link: "/ui/widgets" diff --git a/src/data/sidenav.yml b/src/data/sidenav.yml index 0ae181c0b28..836431d42e7 100644 --- a/src/data/sidenav.yml +++ b/src/data/sidenav.yml @@ -1,90 +1,23 @@ - header: Get started -- title: Set up Flutter - permalink: /get-started - hiddenChildren: true +- title: Install Flutter icon: build children: - - title: Overview - permalink: /get-started + - title: Start here + permalink: /install - title: Quick start - permalink: /get-started/quick + permalink: /install/quick - title: Custom setup - permalink: /get-started/custom - -- title: Install Flutter - permalink: /install - icon: download - children: - - title: Overview - permalink: /install - - title: Install with VS Code - permalink: /install/with-vs-code - - title: Install manually - permalink: /install/manual - - title: Upgrade SDK - permalink: /install/upgrade - - title: SDK archive - permalink: /install/archive - - divider - - title: Add to path - permalink: /install/add-to-path - - title: Troubleshoot - permalink: /install/troubleshoot - - title: Uninstall SDK - permalink: /install/uninstall + permalink: /install/custom - title: Learn Flutter icon: school children: - - title: Introduction - permalink: /get-started/learn-flutter - - title: Write your first app - permalink: /get-started/codelab - - title: Learn the fundamentals - expanded: true - children: - - title: Introduction - permalink: /get-started/fundamentals - - title: Intro to Dart - permalink: /get-started/fundamentals/dart - - title: Widgets - permalink: /get-started/fundamentals/widgets - - title: Layout - permalink: /get-started/fundamentals/layout - - title: State management - permalink: /get-started/fundamentals/state-management - - title: Handling user input - permalink: /get-started/fundamentals/user-input - - title: Networking and data - permalink: /get-started/fundamentals/networking - - title: Local data and caching - permalink: /get-started/fundamentals/local-caching - - - title: From another platform? - permalink: /get-started/flutter-for - children: - - title: Flutter for Android devs - permalink: /get-started/flutter-for/android-devs - - title: Flutter for Jetpack Compose devs - permalink: /get-started/flutter-for/compose-devs - - title: Flutter for SwiftUI devs - permalink: /get-started/flutter-for/swiftui-devs - - title: Flutter for UIKit devs - permalink: /get-started/flutter-for/uikit-devs - - title: Flutter for React Native devs - permalink: /get-started/flutter-for/react-native-devs - - title: Flutter for web devs - permalink: /get-started/flutter-for/web-devs - - title: Flutter for Xamarin.Forms devs - permalink: /get-started/flutter-for/xamarin-forms-devs - - title: Introduction to declarative UI - permalink: /get-started/flutter-for/declarative - - title: Flutter versus Swift concurrency - permalink: /get-started/flutter-for/dart-swift-concurrency - - - divider - - title: Learning resources + - title: Overview + permalink: /learn + - title: Learning pathway + permalink: /learn/pathway + - title: All learning resources permalink: /reference/learning-resources - title: Stay up to date @@ -103,6 +36,9 @@ - title: What's new in the docs permalink: /release/whats-new +- divider +- header: Guides + - title: App solutions icon: apps children: @@ -225,8 +161,33 @@ - title: Firebase AI Logic permalink: https://firebase.google.com/docs/ai-logic/get-started?platform=flutter + +- title: From another platform? + icon: devices + permalink: /flutter-for + children: + - title: Flutter for Android devs + permalink: /flutter-for/android-devs + - title: Flutter for Jetpack Compose devs + permalink: /flutter-for/compose-devs + - title: Flutter for SwiftUI devs + permalink: /flutter-for/swiftui-devs + - title: Flutter for UIKit devs + permalink: /flutter-for/uikit-devs + - title: Flutter for React Native devs + permalink: /flutter-for/react-native-devs + - title: Flutter for web devs + permalink: /flutter-for/web-devs + - title: Flutter for Xamarin.Forms devs + permalink: /flutter-for/xamarin-forms-devs + - title: Introduction to declarative UI + permalink: /flutter-for/declarative + - title: Flutter versus Swift concurrency + permalink: /flutter-for/dart-swift-concurrency + - divider - header: User interface + - title: Introduction permalink: /ui icon: palette