From adf02c2bea36e5ee8fbb94f0ec0199210daa1dbc Mon Sep 17 00:00:00 2001 From: Pierre-Louis Date: Thu, 30 Oct 2025 15:43:32 +0100 Subject: [PATCH 1/5] format .yaml --- examples/app-architecture/todo_data_service/pubspec.yaml | 2 +- examples/cookbook/effects/staggered_menu_animation/pubspec.yaml | 1 - .../get-started/flutter-for/android_devs/analysis_options.yaml | 2 -- examples/tools/pubspec.yaml | 1 - 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/app-architecture/todo_data_service/pubspec.yaml b/examples/app-architecture/todo_data_service/pubspec.yaml index 7299cafd0be..f511df03f67 100644 --- a/examples/app-architecture/todo_data_service/pubspec.yaml +++ b/examples/app-architecture/todo_data_service/pubspec.yaml @@ -23,4 +23,4 @@ dev_dependencies: json_serializable: ^6.10.0 flutter: - uses-material-design: true \ No newline at end of file + uses-material-design: true diff --git a/examples/cookbook/effects/staggered_menu_animation/pubspec.yaml b/examples/cookbook/effects/staggered_menu_animation/pubspec.yaml index f3d1902d58d..d281cc9caf6 100644 --- a/examples/cookbook/effects/staggered_menu_animation/pubspec.yaml +++ b/examples/cookbook/effects/staggered_menu_animation/pubspec.yaml @@ -20,4 +20,3 @@ dev_dependencies: flutter: uses-material-design: true - diff --git a/examples/get-started/flutter-for/android_devs/analysis_options.yaml b/examples/get-started/flutter-for/android_devs/analysis_options.yaml index dd477a43b41..e2badd73ea0 100644 --- a/examples/get-started/flutter-for/android_devs/analysis_options.yaml +++ b/examples/get-started/flutter-for/android_devs/analysis_options.yaml @@ -1,3 +1 @@ include: ../../../analysis_options.yaml - - diff --git a/examples/tools/pubspec.yaml b/examples/tools/pubspec.yaml index e53d395bd6e..7331d926b3a 100644 --- a/examples/tools/pubspec.yaml +++ b/examples/tools/pubspec.yaml @@ -20,4 +20,3 @@ dev_dependencies: flutter: uses-material-design: true - From 2be6e0d6391dedb0bfa58ca9a5d6eb6a651339af Mon Sep 17 00:00:00 2001 From: Pierre-Louis Date: Thu, 30 Oct 2025 15:44:11 +0100 Subject: [PATCH 2/5] format .scss --- site/lib/_sass/base/_base.scss | 67 ++++++++++++------- site/lib/_sass/base/_print-overrides.scss | 18 +++-- site/lib/_sass/base/_reset.scss | 12 +++- site/lib/_sass/base/_root.scss | 3 +- site/lib/_sass/base/_utils.scss | 4 +- site/lib/_sass/components/_banner.scss | 3 +- site/lib/_sass/components/_button.scss | 10 +-- site/lib/_sass/components/_card.scss | 4 +- site/lib/_sass/components/_code.scss | 20 ++++-- site/lib/_sass/components/_content.scss | 22 ++++-- site/lib/_sass/components/_cookie-notice.scss | 1 + site/lib/_sass/components/_dropdown.scss | 3 +- .../lib/_sass/components/_expansion-list.scss | 2 +- site/lib/_sass/components/_footer.scss | 6 +- site/lib/_sass/components/_header.scss | 2 +- site/lib/_sass/components/_next-prev-nav.scss | 5 +- site/lib/_sass/components/_os-selector.scss | 6 +- site/lib/_sass/components/_side-menu.scss | 9 +-- site/lib/_sass/components/_sidebar.scss | 14 ++-- site/lib/_sass/components/_site-switcher.scss | 6 +- site/lib/_sass/components/_tabs.scss | 2 +- site/lib/_sass/components/_theming.scss | 2 +- site/lib/_sass/components/_toc.scss | 9 +-- site/lib/_sass/components/_trailing.scss | 2 +- .../pages/_learning-resources-index.scss | 17 +++-- site/lib/_sass/pages/_search.scss | 10 ++- 26 files changed, 174 insertions(+), 85 deletions(-) diff --git a/site/lib/_sass/base/_base.scss b/site/lib/_sass/base/_base.scss index fcddcfd5513..dbbeada2449 100644 --- a/site/lib/_sass/base/_base.scss +++ b/site/lib/_sass/base/_base.scss @@ -14,7 +14,8 @@ body { // If the TOC is disabled, reduce the subheader height to // ensure offset calculations are still correct. - &[data-toc="false"], &.no-toc { + &[data-toc="false"], + &.no-toc { --site-subheader-height: 0rem; } } @@ -65,10 +66,10 @@ picture { .material-symbols { font-family: var(--site-icon-fontFamily); font-variation-settings: - 'FILL' 0, - 'wght' 400, - 'GRAD' 0, - 'opsz' 24; + 'FILL' 0, + 'wght' 400, + 'GRAD' 0, + 'opsz' 24; font-weight: normal; font-style: normal; @@ -100,7 +101,9 @@ picture { max-width: 320px; } - &--border { border: 1px solid var(--site-inset-borderColor); } + &--border { + border: 1px solid var(--site-inset-borderColor); + } } .site-image-right { @@ -157,8 +160,8 @@ main figure { flex-direction: row; flex-wrap: wrap; - > div { - width: 100%; + >div { + width: 100%; &:last-child { text-align: center; @@ -166,7 +169,7 @@ main figure { } @media(min-width: 769px) { - > div { + >div { &:first-child { flex: 0 0 58%; max-width: 58%; @@ -203,7 +206,9 @@ main figure { } table { - td, th { + + td, + th { padding: .25rem; } } @@ -232,7 +237,8 @@ table { } } - td, th { + td, + th { border-top: 1px solid var(--site-inset-borderColor); padding: .75rem; } @@ -244,7 +250,8 @@ thead:has(th:empty) { display: none; } -.table-wrapper, .scrollable-table { +.table-wrapper, +.scrollable-table { overflow-x: auto; margin-block-start: 1rem; margin-block-end: 1rem; @@ -274,7 +281,7 @@ blockquote { details { margin-bottom: 0.75rem; - > summary { + >summary { font-weight: 500; user-select: none; border-radius: 0.125rem; @@ -288,7 +295,7 @@ details { &[open] { margin-bottom: unset; - > summary { + >summary { margin-bottom: 0.75rem; } } @@ -315,7 +322,7 @@ ol.steps { $step-indicator-height: 1.75rem; li { - > * { + >* { &:first-child { margin-block-start: 0; } @@ -330,22 +337,23 @@ ol.steps { } } - ul > li { + ul>li { &:not(:last-child) { padding-block-end: .5rem; } } - > li { + >li { position: relative; counter-increment: steps; - ol, ul { + ol, + ul { padding-inline-start: 2rem; margin-block-end: 0.75rem; } - > h3:first-child { + >h3:first-child { display: flex; align-items: center; height: $step-indicator-height; @@ -397,18 +405,25 @@ ol.steps { } main { - ol + img, ul + img, ol + p, ul + p, p + p + img { + + ol+img, + ul+img, + ol+p, + ul+p, + p+p+img { margin-bottom: 1rem; } td { - > *:last-child { + >*:last-child { margin-bottom: 0; } } } -p + ul, p + ol, p + dl { +p+ul, +p+ol, +p+dl { margin-block-start: 0.75rem; margin-block-end: 0.75rem; } @@ -421,6 +436,7 @@ p + ul, p + ol, p + dl { .card-os-bug { position: relative; + &::before { content: ""; position: absolute; @@ -432,12 +448,14 @@ p + ul, p + ol, p + dl { background-size: contain; background-color: transparent; background-repeat: no-repeat; - z-index: var(--site-z-floating); /* Place in front of card */ + z-index: var(--site-z-floating); + /* Place in front of card */ } } .card-macos { @extend .card-os-bug; + &::before { background-image: url("/assets/images/docs/brand-svg/macos-bug.svg"); } @@ -445,6 +463,7 @@ p + ul, p + ol, p + dl { .card-windows { @extend .card-os-bug; + &::before { background-image: url("/assets/images/docs/brand-svg/windows-bug.svg"); } @@ -452,6 +471,7 @@ p + ul, p + ol, p + dl { .card-linux { @extend .card-os-bug; + &::before { background-image: url("/assets/images/docs/brand-svg/linux.svg"); } @@ -459,6 +479,7 @@ p + ul, p + ol, p + dl { .card-chromeos { @extend .card-os-bug; + &::before { background-image: url("/assets/images/docs/brand-svg/chromeos.svg"); } diff --git a/site/lib/_sass/base/_print-overrides.scss b/site/lib/_sass/base/_print-overrides.scss index fa5ac4e8a80..fab0e919a3a 100644 --- a/site/lib/_sass/base/_print-overrides.scss +++ b/site/lib/_sass/base/_print-overrides.scss @@ -1,9 +1,18 @@ // Overrides for printing so only page content is printed @media print { + // Ignore navigation elements and other non-necessary interactive ones - #site-header, .subnav, .site-footer, #sidenav, .navbar, - #site-toc--side, #page-github-links, #cookie-notice, .site-banner, - .code-excerpt__copy-btn, .breadcrumb { + #site-header, + .subnav, + .site-footer, + #sidenav, + .navbar, + #site-toc--side, + #page-github-links, + #cookie-notice, + .site-banner, + .code-excerpt__copy-btn, + .breadcrumb { display: none !important; } @@ -27,7 +36,8 @@ } // Show borders around notes and code blocks - .alert, pre { + .alert, + pre { border: 1px solid black; } } diff --git a/site/lib/_sass/base/_reset.scss b/site/lib/_sass/base/_reset.scss index 27338f34c10..61d4b5cdd8e 100644 --- a/site/lib/_sass/base/_reset.scss +++ b/site/lib/_sass/base/_reset.scss @@ -1,16 +1,22 @@ -*, ::before, ::after { +*, +::before, +::after { box-sizing: border-box; border-width: 0; } -html, body { +html, +body { padding: 0; margin: 0; -webkit-text-size-adjust: none; text-size-adjust: none; } -button, input, select, textarea { +button, +input, +select, +textarea { font-family: inherit; font-feature-settings: inherit; font-variation-settings: inherit; diff --git a/site/lib/_sass/base/_root.scss b/site/lib/_sass/base/_root.scss index d6faf890bf9..14dff029b21 100644 --- a/site/lib/_sass/base/_root.scss +++ b/site/lib/_sass/base/_root.scss @@ -161,7 +161,8 @@ body { --site-alert-warning-color: #cea11f; --site-alert-error-color: #ff5d5d; - .opal, .opal span { + .opal, + .opal span { color: var(--opal-dark-color) !important; font-style: var(--opal-dark-font-style) !important; font-weight: var(--opal-dark-font-weight) !important; diff --git a/site/lib/_sass/base/_utils.scss b/site/lib/_sass/base/_utils.scss index dae6edac4f8..6bc90882928 100644 --- a/site/lib/_sass/base/_utils.scss +++ b/site/lib/_sass/base/_utils.scss @@ -28,7 +28,7 @@ main { margin-block-end: 0; } - > * { + >* { max-width: 100%; min-width: 0; } @@ -41,7 +41,7 @@ main { justify-content: space-around; margin: 1rem; - > figure { + >figure { margin-block-end: 0; } } diff --git a/site/lib/_sass/components/_banner.scss b/site/lib/_sass/components/_banner.scss index 133a6c2acbc..218afdf51d9 100644 --- a/site/lib/_sass/components/_banner.scss +++ b/site/lib/_sass/components/_banner.scss @@ -22,7 +22,8 @@ text-wrap: balance; } - a, button { + a, + button { white-space: nowrap; color: var(--site-onPrimary-color); font-family: var(--site-ui-fontFamily); diff --git a/site/lib/_sass/components/_button.scss b/site/lib/_sass/components/_button.scss index 784c029a4b8..d0ba9f2e55a 100644 --- a/site/lib/_sass/components/_button.scss +++ b/site/lib/_sass/components/_button.scss @@ -14,16 +14,18 @@ a { } &:visited { - color:var(--site-link-fgColor); + color: var(--site-link-fgColor); } } -a, button { +a, +button { text-decoration: none; background: none; cursor: pointer; - &.filled-button, &.text-button { + &.filled-button, + &.text-button { display: flex; align-items: center; width: fit-content; @@ -78,7 +80,7 @@ a, button { -webkit-user-select: none; background: none; - > span { + >span { font-size: 1.75rem; } diff --git a/site/lib/_sass/components/_card.scss b/site/lib/_sass/components/_card.scss index 630cdf4e94e..913e5fedbab 100644 --- a/site/lib/_sass/components/_card.scss +++ b/site/lib/_sass/components/_card.scss @@ -20,6 +20,7 @@ &.very-wide { --card-min-width: 32rem; + .card { min-height: 7.5rem; } @@ -184,7 +185,8 @@ } } - a.card, button.card { + a.card, + button.card { text-decoration: none; .card-title { diff --git a/site/lib/_sass/components/_code.scss b/site/lib/_sass/components/_code.scss index fde9f54d64c..66ef2bd3219 100644 --- a/site/lib/_sass/components/_code.scss +++ b/site/lib/_sass/components/_code.scss @@ -1,12 +1,16 @@ @use 'sass:color'; -pre, code, kbd, samp { +pre, +code, +kbd, +samp { font-family: var(--site-code-fontFamily); } // Inline code spans found within the primary contents of the page. // Targets all elements, but
 shouldn't have many of these.
-main code, main kbd {
+main code,
+main kbd {
   font-size: 0.9em;
   line-height: 1.25em;
   padding: 0.05rem 0.2rem;
@@ -36,7 +40,7 @@ pre {
   font-weight: 400;
   padding: 1.25rem;
 
-  > code {
+  >code {
     // Undo the core `code` styles.
     font-size: 0.9125rem;
     line-height: 1.75em;
@@ -87,7 +91,8 @@ pre {
       border-left-color: var(--site-primary-color);
     }
 
-    &.added-line, &.removed-line {
+    &.added-line,
+    &.removed-line {
       padding-left: 0.25rem;
 
       &::before {
@@ -201,12 +206,15 @@ pre {
       opacity: 0;
       transition: opacity 0.4s;
 
-      &:hover, &:focus, &:active {
+      &:hover,
+      &:focus,
+      &:active {
         opacity: 1;
       }
     }
 
-    &:hover, &:focus-within {
+    &:hover,
+    &:focus-within {
       .code-block-language {
         opacity: 0;
       }
diff --git a/site/lib/_sass/components/_content.scss b/site/lib/_sass/components/_content.scss
index 3598d7668a1..d219a09cef4 100644
--- a/site/lib/_sass/components/_content.scss
+++ b/site/lib/_sass/components/_content.scss
@@ -41,7 +41,8 @@
       }
     }
 
-    > img, > lite-youtube {
+    >img,
+    >lite-youtube {
       margin-bottom: 1rem;
     }
 
@@ -63,11 +64,17 @@
     margin-block-start: 0;
   }
 
-  b, strong {
+  b,
+  strong {
     font-weight: bolder;
   }
 
-  h1, h2, h3, h4, h5, h6 {
+  h1,
+  h2,
+  h3,
+  h4,
+  h5,
+  h6 {
     text-wrap: balance;
     scroll-margin-top: 7rem;
     line-height: 1.2;
@@ -112,7 +119,12 @@
     margin-block-end: 0.75rem;
     align-items: center;
 
-    h1, h2, h3, h4, h5, h6 {
+    h1,
+    h2,
+    h3,
+    h4,
+    h5,
+    h6 {
       margin-top: 0;
       margin-bottom: 0;
     }
@@ -160,7 +172,7 @@ nav.breadcrumbs {
   align-items: center;
   margin-block-end: 1rem;
 
-  > ol {
+  >ol {
     border-radius: 0.375rem;
     margin-block-start: 0;
     padding: 0.375rem 0;
diff --git a/site/lib/_sass/components/_cookie-notice.scss b/site/lib/_sass/components/_cookie-notice.scss
index 8f6af0e6dea..d1ef80b7c53 100644
--- a/site/lib/_sass/components/_cookie-notice.scss
+++ b/site/lib/_sass/components/_cookie-notice.scss
@@ -14,6 +14,7 @@
     0% {
       opacity: 0;
     }
+
     100% {
       opacity: 1;
     }
diff --git a/site/lib/_sass/components/_dropdown.scss b/site/lib/_sass/components/_dropdown.scss
index c412916ca40..c6efc6934b3 100644
--- a/site/lib/_sass/components/_dropdown.scss
+++ b/site/lib/_sass/components/_dropdown.scss
@@ -33,7 +33,8 @@
         li {
           padding: 0.25rem;
 
-          a, button {
+          a,
+          button {
             display: flex;
             align-items: center;
             flex-direction: row;
diff --git a/site/lib/_sass/components/_expansion-list.scss b/site/lib/_sass/components/_expansion-list.scss
index fe8800dea10..7c3986cf18f 100644
--- a/site/lib/_sass/components/_expansion-list.scss
+++ b/site/lib/_sass/components/_expansion-list.scss
@@ -7,7 +7,7 @@
   margin-block-end: 1.5rem;
 
   .expansion-panel {
-    > a {
+    >a {
       display: flex;
       align-items: center;
       justify-content: space-between;
diff --git a/site/lib/_sass/components/_footer.scss b/site/lib/_sass/components/_footer.scss
index 604e71a379f..44015de398a 100644
--- a/site/lib/_sass/components/_footer.scss
+++ b/site/lib/_sass/components/_footer.scss
@@ -128,8 +128,10 @@
     color: var(--site-footer-fgColor);
     font-family: var(--site-ui-fontFamily);
 
-    &:hover, &:focus, &:active {
+    &:hover,
+    &:focus,
+    &:active {
       color: var(--site-filledButton-fgColor);
     }
   }
-}
\ No newline at end of file
+}
diff --git a/site/lib/_sass/components/_header.scss b/site/lib/_sass/components/_header.scss
index 1617b237bd1..03b28663534 100644
--- a/site/lib/_sass/components/_header.scss
+++ b/site/lib/_sass/components/_header.scss
@@ -149,7 +149,7 @@ body.open_menu #menu-toggle span.material-symbols {
     text-decoration: none;
   }
 
-  > span {
+  >span {
     &.name {
       display: none;
 
diff --git a/site/lib/_sass/components/_next-prev-nav.scss b/site/lib/_sass/components/_next-prev-nav.scss
index e532c789c19..ce2bde478b8 100644
--- a/site/lib/_sass/components/_next-prev-nav.scss
+++ b/site/lib/_sass/components/_next-prev-nav.scss
@@ -22,7 +22,8 @@
   @media (min-width: 576px) {
     flex-wrap: nowrap;
 
-    .prev, .next {
+    .prev,
+    .next {
       max-width: 50%;
     }
   }
@@ -42,7 +43,7 @@
     padding: 0.5rem;
     min-height: 100%;
 
-    > div {
+    >div {
       display: flex;
       flex-direction: column;
       padding: 0.5rem;
diff --git a/site/lib/_sass/components/_os-selector.scss b/site/lib/_sass/components/_os-selector.scss
index 0f95d152757..9768c1c5e51 100644
--- a/site/lib/_sass/components/_os-selector.scss
+++ b/site/lib/_sass/components/_os-selector.scss
@@ -1,5 +1,9 @@
 body {
-  .windows-only, .macos-only, .linux-only, .chromeos-only {
+
+  .windows-only,
+  .macos-only,
+  .linux-only,
+  .chromeos-only {
     display: none;
   }
 
diff --git a/site/lib/_sass/components/_side-menu.scss b/site/lib/_sass/components/_side-menu.scss
index f904f69ca55..eba9b9a7b0d 100644
--- a/site/lib/_sass/components/_side-menu.scss
+++ b/site/lib/_sass/components/_side-menu.scss
@@ -2,17 +2,18 @@
   margin: 0;
   --toc-indent: 0;
 
-  > li {
+  >li {
     li {
       --toc-indent: 1;
 
-      > li {
+      >li {
         --toc-indent: 2;
       }
     }
   }
 
-  &, ul {
+  &,
+  ul {
     padding: 0;
     font-size: 0.875rem;
     list-style: none;
@@ -75,7 +76,7 @@
     display: flex;
   }
 
-  > nav {
+  >nav {
     header {
       font-size: 1rem;
       margin-block-end: 0.25rem;
diff --git a/site/lib/_sass/components/_sidebar.scss b/site/lib/_sass/components/_sidebar.scss
index bf988cbceef..d89f22b1296 100644
--- a/site/lib/_sass/components/_sidebar.scss
+++ b/site/lib/_sass/components/_sidebar.scss
@@ -74,7 +74,8 @@
       margin-top: 0.5rem;
     }
 
-    a.nav-link, button.nav-link {
+    a.nav-link,
+    button.nav-link {
       background: none;
       border: none;
       text-wrap: pretty;
@@ -97,7 +98,7 @@
       font-family: var(--site-ui-fontFamily);
       text-decoration: none;
 
-      > div {
+      >div {
         display: inline-block;
 
         .material-symbols {
@@ -116,7 +117,7 @@
         transition: transform .3s ease-in-out;
       }
 
-      + ul {
+      +ul {
         display: none;
 
         &.show {
@@ -149,9 +150,10 @@
     }
   }
 
-  nav > ul {
+  nav>ul {
+
     // Only apply styles to top-level entries.
-    > li {
+    >li {
       font-size: 1rem;
     }
 
@@ -159,7 +161,7 @@
       margin-left: 0.675rem;
     }
 
-    > .nav-header {
+    >.nav-header {
       font-size: 1.075rem;
     }
   }
diff --git a/site/lib/_sass/components/_site-switcher.scss b/site/lib/_sass/components/_site-switcher.scss
index acea995ad25..58b3fb0e331 100644
--- a/site/lib/_sass/components/_site-switcher.scss
+++ b/site/lib/_sass/components/_site-switcher.scss
@@ -14,12 +14,14 @@
   }
 
   display: none;
+
   @media (min-width: 320px) {
     display: block;
   }
 }
 
-#site-primary-logo, #site-switcher .site-wordmark {
+#site-primary-logo,
+#site-switcher .site-wordmark {
   padding: 0.4rem 0.6rem;
   align-items: center;
   display: flex;
@@ -34,7 +36,7 @@
   font-family: 'Google Sans', sans-serif;
   user-select: none;
 
-  > img {
+  >img {
     width: 28px;
     margin-right: 0.25rem;
   }
diff --git a/site/lib/_sass/components/_tabs.scss b/site/lib/_sass/components/_tabs.scss
index 4a03086f17a..88e0a49d104 100644
--- a/site/lib/_sass/components/_tabs.scss
+++ b/site/lib/_sass/components/_tabs.scss
@@ -79,7 +79,7 @@ ul.nav-tabs {
       padding-left: 1rem;
     }
 
-    .tab-pane > :first-child {
+    .tab-pane> :first-child {
       margin-block-start: 0;
     }
   }
diff --git a/site/lib/_sass/components/_theming.scss b/site/lib/_sass/components/_theming.scss
index 68ada3c8a3d..23458610eaa 100644
--- a/site/lib/_sass/components/_theming.scss
+++ b/site/lib/_sass/components/_theming.scss
@@ -1,7 +1,7 @@
 #theme-switcher {
   position: relative;
 
-  > .dropdown-content {
+  >.dropdown-content {
     right: -0.5rem;
 
     .material-symbols {
diff --git a/site/lib/_sass/components/_toc.scss b/site/lib/_sass/components/_toc.scss
index b3012a90d33..8d183cf9b33 100644
--- a/site/lib/_sass/components/_toc.scss
+++ b/site/lib/_sass/components/_toc.scss
@@ -26,7 +26,7 @@
   font-size: 0.875rem;
   z-index: var(--site-z-subheader);
 
-  > button.dropdown-button {
+  >button.dropdown-button {
     display: flex;
     flex-direction: row;
     align-items: center;
@@ -36,7 +36,7 @@
     border-radius: 0;
     margin: 2px;
 
-    > span {
+    >span {
       display: flex;
       flex-direction: row;
       align-items: center;
@@ -63,6 +63,7 @@
     overflow: hidden;
 
     display: none;
+
     @media (min-width: 320px) {
       display: flex;
     }
@@ -105,7 +106,7 @@
       max-width: 24rem;
     }
 
-    > a {
+    >a {
       margin: 0.4rem 0;
       padding: 0.1rem;
       font-size: 1rem;
@@ -133,7 +134,7 @@
       }
     }
 
-    > nav {
+    >nav {
       padding: 0.6rem 0 0.8rem;
     }
   }
diff --git a/site/lib/_sass/components/_trailing.scss b/site/lib/_sass/components/_trailing.scss
index 923d6d791d8..9474471ab19 100644
--- a/site/lib/_sass/components/_trailing.scss
+++ b/site/lib/_sass/components/_trailing.scss
@@ -15,7 +15,7 @@
   padding: 0.25rem;
   height: 4.25rem;
 
-  > .feedback {
+  >.feedback {
     display: flex;
     flex-direction: column;
     align-items: center;
diff --git a/site/lib/_sass/pages/_learning-resources-index.scss b/site/lib/_sass/pages/_learning-resources-index.scss
index 4cd22a9530e..74a1eaaffc5 100644
--- a/site/lib/_sass/pages/_learning-resources-index.scss
+++ b/site/lib/_sass/pages/_learning-resources-index.scss
@@ -60,11 +60,16 @@
     z-index: 1000;
   }
 
-  #open-filter-toggle:not(:checked) + #resource-filter-group-wrapper { right: -220px; }
-  #open-filter-toggle:checked + #resource-filter-group-wrapper { right: 0; }
+  #open-filter-toggle:not(:checked)+#resource-filter-group-wrapper {
+    right: -220px;
+  }
+
+  #open-filter-toggle:checked+#resource-filter-group-wrapper {
+    right: 0;
+  }
 }
 
-//Desktop screens 
+//Desktop screens
 @media (min-width: 840px) {
   #resource-filter-group {
     position: static !important;
@@ -72,7 +77,7 @@
   }
 
   .filter-header {
-    display: none !important; 
+    display: none !important;
   }
 }
 
@@ -101,6 +106,7 @@
         0% {
           right: -10rem;
         }
+
         100% {
           right: 0;
         }
@@ -262,7 +268,8 @@
         color: var(--site-onPrimary-color-light);
       }
 
-      &:disabled, &[disabled] {
+      &:disabled,
+      &[disabled] {
         color: var(--site-inset-bgColor-translucent);
         cursor: default;
       }
diff --git a/site/lib/_sass/pages/_search.scss b/site/lib/_sass/pages/_search.scss
index 4ba67ad462b..30fa71479cc 100644
--- a/site/lib/_sass/pages/_search.scss
+++ b/site/lib/_sass/pages/_search.scss
@@ -60,7 +60,8 @@
     }
   }
 
-  .gsc-result-info, .gs-spelling {
+  .gsc-result-info,
+  .gs-spelling {
     color: var(--site-base-fgColor-lighter);
   }
 
@@ -71,7 +72,9 @@
   }
 
   .gsc-webResult {
-    .gs-title, .gs-title * {
+
+    .gs-title,
+    .gs-title * {
       color: var(--site-link-fgColor);
     }
 
@@ -102,7 +105,8 @@
     }
   }
 
-  .gcsc-find-more-on-google, .gcsc-find-more-on-google * {
+  .gcsc-find-more-on-google,
+  .gcsc-find-more-on-google * {
     color: var(--site-link-fgColor);
     fill: var(--site-link-fgColor);
   }

From e54a4ff97201cf2d5f83b80d6469e6e0f0896c70 Mon Sep 17 00:00:00 2001
From: Pierre-Louis 
Date: Thu, 30 Oct 2025 15:50:30 +0100
Subject: [PATCH 3/5] format .md

---
 examples/ui/adaptive_app_demos/README.md      |   3 +-
 prompts/llmstxt_instructions.md               |   2 +-
 src/_includes/docs/china-notice-cn.md         |   2 +-
 src/_includes/docs/code-and-image.md          |   1 -
 .../networking/internet-permission.md         |   4 +-
 .../debug/debug-android-attach-process.md     |   6 +-
 .../docs/debug/debug-flow-xcode-as-start.md   |   2 +-
 .../resource-links/ffi-video-resources.md     |   2 +-
 .../how-to-enable-disable.md                  |   2 +-
 .../migrate-objective-c-plugin.md             |  12 +-
 .../migrate-swift-plugin.md                   |  10 +-
 .../docs/test/integration/macos-example.md    |   4 +-
 src/_includes/docs/tutorial/game-code.md      |   8 +-
 .../docs/vscode-flutter-bar/hot-reload.md     |   2 +-
 .../docs/vscode-flutter-bar/hot-restart.md    |   2 +-
 .../docs/vscode-flutter-bar/inspector.md      |   2 +-
 .../docs/vscode-flutter-bar/pause.md          |   2 +-
 src/_includes/docs/vscode-flutter-bar/play.md |   2 +-
 .../docs/vscode-flutter-bar/step-into.md      |   2 +-
 .../docs/vscode-flutter-bar/step-out.md       |   2 +-
 .../docs/vscode-flutter-bar/step-over.md      |   2 +-
 src/_includes/docs/vscode-flutter-bar/stop.md |   2 +-
 .../add-to-app/android/plugin-setup.md        |   5 +-
 src/content/ai-toolkit/chat-client-sample.md  |   1 -
 .../ai-toolkit/custom-llm-providers.md        |   2 +-
 src/content/ai-toolkit/feature-integration.md |   8 +-
 src/content/ai/ai-rules.md                    |   2 +-
 src/content/ai/create-with-ai.md              |   6 +-
 .../app-architecture/case-study/testing.md    |   2 +-
 .../design-patterns/command.md                |  98 +++----
 .../design-patterns/key-value-data.md         | 108 +++----
 .../design-patterns/offline-first.md          | 268 +++++++++---------
 .../design-patterns/optimistic-state.md       | 214 +++++++-------
 .../design-patterns/result.md                 | 136 ++++-----
 .../app-architecture/design-patterns/sql.md   | 132 ++++-----
 src/content/brand/index.md                    |   1 -
 src/content/community/china/index.md          |  26 +-
 src/content/contribute/docs/sidenav.md        |   2 -
 src/content/cookbook/design/fonts.md          |  32 +--
 src/content/cookbook/design/orientation.md    |   6 +-
 .../cookbook/effects/download-button.md       |  40 +--
 src/content/cookbook/effects/drag-a-widget.md |  26 +-
 .../cookbook/effects/expandable-fab.md        |   2 +-
 src/content/cookbook/effects/nested-nav.md    |  48 ++--
 .../cookbook/effects/shimmer-loading.md       |  46 +--
 .../effects/staggered-menu-animation.md       |  24 +-
 .../cookbook/forms/text-field-changes.md      |   2 +-
 .../games/achievements-leaderboard.md         |  18 +-
 .../cookbook/games/firestore-multiplayer.md   |   4 +-
 src/content/cookbook/lists/basic-list.md      |   2 +-
 src/content/cookbook/lists/spaced-items.md    |   8 +-
 .../cookbook/maintenance/error-reporting.md   |   2 +-
 .../cookbook/navigation/passing-data.md       |   2 +-
 .../cookbook/navigation/set-up-app-links.md   |  10 +-
 src/content/cookbook/persistence/sqlite.md    |   2 +-
 .../cookbook/plugins/google-mobile-ads.md     |  28 +-
 src/content/cookbook/plugins/play-video.md    |   4 +-
 .../testing/integration/introduction.md       |   2 +-
 .../cookbook/testing/integration/profiling.md |  10 +-
 .../cookbook/testing/unit/introduction.md     |   2 +-
 src/content/cookbook/testing/unit/mocking.md  |   2 +-
 .../cookbook/testing/widget/orientation.md    |  14 +-
 .../cookbook/testing/widget/scrolling.md      |   2 +-
 .../cookbook/testing/widget/tap-drag.md       |   1 -
 src/content/dash/index.md                     |  10 +-
 src/content/data-and-backend/google-apis.md   |   2 +-
 src/content/data-and-backend/networking.md    |   4 +-
 .../data-and-backend/serialization/json.md    |  20 +-
 .../data-and-backend/state-mgmt/simple.md     |  10 +-
 src/content/deployment/android.md             |   4 +-
 src/content/deployment/cd.md                  |  16 +-
 src/content/deployment/flavors-ios.md         |  14 +-
 src/content/deployment/flavors.md             |   4 +-
 src/content/deployment/linux.md               |  64 ++---
 src/content/deployment/macos.md               |  20 +-
 src/content/deployment/obfuscate.md           |   4 +-
 src/content/deployment/web.md                 |   2 +-
 src/content/embedded/index.md                 |   4 +-
 .../get-started/flutter-for/android-devs.md   |  22 +-
 .../get-started/flutter-for/compose-devs.md   |  80 +++---
 .../flutter-for/dart-swift-concurrency.md     | 154 +++++-----
 .../get-started/flutter-for/declarative.md    |   1 -
 .../get-started/flutter-for/swiftui-devs.md   |  12 +-
 .../get-started/flutter-for/uikit-devs.md     |   2 +-
 .../get-started/flutter-for/web-devs.md       |   3 +-
 .../flutter-for/xamarin-forms-devs.md         |   2 +-
 src/content/get-started/fundamentals/dart.md  |  94 +++---
 .../get-started/fundamentals/local-caching.md |   8 +-
 .../get-started/fundamentals/networking.md    |   8 +-
 .../get-started/fundamentals/widgets.md       |   2 +-
 src/content/get-started/learn-flutter.md      |  24 +-
 src/content/jobs/_template.md                 |   2 +-
 src/content/jobs/tech_writer_ii.md            |   2 +-
 .../background-processes.md                   |   8 +-
 .../packages-and-plugins/using-packages.md    |  24 +-
 src/content/perf/deferred-components.md       |   4 +-
 src/content/perf/impeller.md                  |   2 +-
 src/content/perf/isolates.md                  |   2 +-
 src/content/perf/metrics.md                   |  10 +-
 src/content/perf/ui-performance.md            |   6 +-
 src/content/perf/web-performance.md           |   2 +-
 .../android/call-jetpack-apis.md              |   6 +-
 .../android/compose-activity.md               |  22 +-
 .../android/platform-views.md                 |   4 +-
 .../android/predictive-back.md                |   1 -
 .../android/restore-state-android.md          |   4 +-
 .../platform-integration/ios/ios-debugging.md |   1 -
 .../ios/restore-state-ios.md                  |   5 +-
 .../macos/platform-views.md                   |   4 +-
 .../platform-integration/web/building.md      |   1 -
 src/content/platform-integration/web/faq.md   |   4 +-
 .../platform-integration/web/renderers.md     |   6 +-
 src/content/platform-integration/web/wasm.md  |   2 +-
 .../web/web-content-in-flutter.md             |   2 +-
 .../web/web-dev-config-file.md                |   2 +-
 .../platform-integration/web/web-images.md    |   8 +-
 .../platform-integration/windows/building.md  |   8 +-
 src/content/reference/crash-reporting.md      |  12 +-
 .../reference/security-false-positives.md     |   4 +-
 src/content/reference/user-surveys.md         |  18 +-
 src/content/release/archive-whats-new.md      |   8 +-
 .../breaking-changes/1-22-deprecations.md     |   8 +-
 .../breaking-changes/3-10-deprecations.md     |   4 +-
 .../breaking-changes/3-13-deprecations.md     |   2 +-
 .../breaking-changes/3-16-deprecations.md     |   4 +-
 .../breaking-changes/3-19-deprecations.md     |   6 +-
 .../breaking-changes/3-7-deprecations.md      |   2 +-
 .../add-showAutocorrectionPromptRect.md       |  12 +-
 ...oid-14-nonlinear-text-scaling-migration.md |   4 +-
 .../android-java-gradle-migration-guide.md    |  18 +-
 .../android-predictive-back.md                |   2 +-
 ...d-setIsRunningInRobolectricTest-removed.md |   4 +-
 .../animation-sheet-builder-display.md        |   2 +-
 .../bottom-navigation-title-to-label.md       |   8 +-
 .../component-theme-normalization-updates.md  |   1 -
 .../component-theme-normalization.md          |   2 +-
 .../release/breaking-changes/context-menus.md |   2 +-
 .../default-desktop-scrollbars.md             |   4 +-
 .../default-scroll-behavior-drag.md           |   8 +-
 .../deprecate-overlay-portal-targets-root.md  |   2 +-
 .../deprecate-textscalefactor.md              | 101 ++++---
 .../release/breaking-changes/describe-enum.md |   2 +-
 .../release/breaking-changes/dispose.md       |   4 +-
 .../editable-text-focus-attachment.md         |   2 +-
 .../enterText-trailing-caret.md               |  12 +-
 .../expansion-tile-controller.md              |   6 +-
 .../flutter-driver-migration.md               |   6 +-
 .../flutter-gradle-plugin-apply.md            |  16 +-
 .../flutter-memory-allocations.md             |   6 +-
 .../breaking-changes/form-semantics.md        |   2 +-
 .../ignoringsemantics-migration.md            |   2 +-
 .../insert-content-text-input-client.md       |   2 +-
 ...iewcontroller-splashscreenview-nullable.md |   2 +-
 .../breaking-changes/kotlin-version.md        |   2 +-
 .../macos-windows-merged-threads.md           |   3 +-
 .../breaking-changes/material-3-default.md    |   1 -
 .../material-design-3-token-update.md         |   2 +-
 .../material-theme-system-updates.md          |  10 +-
 .../breaking-changes/menus-text-style.md      |  18 +-
 .../mouse-tracker-moved-to-rendering.md       |  10 +-
 ...-tracker-no-longer-attaches-annotations.md |   2 +-
 .../breaking-changes/multi-touch-scrolling.md |   2 +-
 .../new-color-scheme-roles.md                 |   4 +-
 .../breaking-changes/plugin-api-migration.md  |   4 +-
 .../breaking-changes/popscope-with-result.md  |   2 +-
 .../rendereditable-layout-before-hit-test.md  |   2 +-
 .../route-navigator-refactoring.md            |   2 +-
 .../breaking-changes/scaffold-messenger.md    |   2 +-
 .../scribble-text-input-client.md             |   4 +-
 .../snackbar-with-action-behavior-update.md   |  14 +-
 .../tooltip-semantics-order.md                |   4 +-
 .../breaking-changes/trackpad-gestures.md     |   2 +-
 .../visibility-maintainfocusability.md        |   4 +-
 .../wide-gamut-cupertino-dynamic-color.md     |   4 +-
 .../breaking-changes/window-singleton.md      |   2 +-
 .../release/breaking-changes/zone-errors.md   |   2 +-
 src/content/release/compatibility-policy.md   |   4 +-
 .../changelogs/changelog-1.12.13.md           |   1 -
 .../changelogs/changelog-1.2.1.md             |   2 +-
 .../changelogs/changelog-1.5.4.md             |   2 +-
 .../changelogs/changelog-1.7.8.md             |   2 +-
 .../changelogs/changelog-1.9.1.md             |   3 +-
 .../release-notes-0.0.21-1.0.0.md             |   9 +-
 .../release-notes/release-notes-1.12.13.md    |  14 +-
 .../release-notes/release-notes-1.17.0.md     |   8 +-
 .../release-notes/release-notes-1.2.1.md      |  56 ++--
 .../release-notes/release-notes-1.20.0.md     |   9 +-
 .../release-notes/release-notes-1.22.0.md     |   8 +-
 .../release-notes/release-notes-1.5.4.md      |   2 +-
 .../release-notes/release-notes-1.9.1.md      |   2 +-
 .../release-notes/release-notes-2.0.0.md      |   7 +-
 .../release-notes/release-notes-2.10.0.md     |   9 +-
 .../release-notes/release-notes-2.2.0.md      |   7 +-
 .../release-notes/release-notes-2.5.0.md      |   9 +-
 .../release-notes/release-notes-2.8.0.md      |   9 +-
 .../release-notes/release-notes-3.10.0.md     |   1 -
 .../release-notes/release-notes-3.13.0.md     |   1 -
 .../release-notes/release-notes-archive.md    |   2 +-
 src/content/release/whats-new.md              |   1 -
 .../resources/architectural-overview.md       |   2 +-
 src/content/resources/bug-reports.md          |   2 +-
 src/content/resources/courses.md              |   2 +-
 src/content/resources/faq.md                  |   6 +-
 src/content/resources/games-toolkit.md        |   2 +-
 src/content/security/index.md                 |   5 +-
 src/content/testing/code-debugging.md         |   2 +-
 src/content/testing/common-errors.md          |  28 +-
 src/content/testing/debugging.md              |   2 +-
 .../testing/integration-tests/index.md        |  16 +-
 src/content/testing/testing-plugins.md        |   1 -
 .../testing/vscode-flutter-bar/_hot-reload.md |   2 +-
 .../vscode-flutter-bar/_hot-restart.md        |   2 +-
 .../testing/vscode-flutter-bar/_inspector.md  |   2 +-
 .../testing/vscode-flutter-bar/_pause.md      |   2 +-
 .../testing/vscode-flutter-bar/_play.md       |   2 +-
 .../testing/vscode-flutter-bar/_step-into.md  |   2 +-
 .../testing/vscode-flutter-bar/_step-out.md   |   2 +-
 .../testing/vscode-flutter-bar/_step-over.md  |   2 +-
 .../testing/vscode-flutter-bar/_stop.md       |   2 +-
 src/content/tools/devtools/cli.md             |   6 +-
 src/content/tools/devtools/console.md         |   3 +-
 src/content/tools/devtools/cpu-profiler.md    |   6 +-
 src/content/tools/devtools/deep-links.md      |   3 +-
 src/content/tools/devtools/extensions.md      |   2 +-
 src/content/tools/devtools/inspector.md       |  14 +-
 .../tools/devtools/legacy-inspector.md        |   2 +-
 src/content/tools/devtools/performance.md     |   4 +-
 src/content/tools/property-editor.md          |  16 +-
 src/content/tools/pubspec.md                  |   8 +-
 src/content/tools/sdk.md                      |   2 +-
 src/content/tools/vs-code.md                  |   2 +-
 src/content/tools/widget-previewer.md         |  18 +-
 src/content/tutorial/index.md                 |  28 +-
 .../tutorial/state/1-set-up-project.md        |  10 +-
 src/content/tutorial/state/2-http-requests.md |  12 +-
 .../tutorial/state/3-change-notifier.md       |  14 +-
 .../tutorial/state/4-listenable-builder.md    |   4 +-
 src/content/tutorial/ui-102/1-intro.md        |   2 +-
 .../tutorial/ui-102/2-adaptive-layout.md      |   8 +-
 src/content/tutorial/ui-102/3-slivers.md      |   8 +-
 src/content/tutorial/ui-102/4-navigation.md   |   6 +-
 src/content/tutorial/ui/1-create-an-app.md    |  10 +-
 .../tutorial/ui/2-widget-fundamentals.md      |  20 +-
 src/content/tutorial/ui/3-layout.md           |  22 +-
 src/content/tutorial/ui/4-devtools.md         |   8 +-
 src/content/tutorial/ui/5-user-input.md       |  50 ++--
 src/content/tutorial/ui/6-stateful-widget.md  |  14 +-
 .../tutorial/ui/7-implicit-animations.md      |   4 +-
 src/content/ui/accessibility/index.md         |   4 +-
 .../ui/accessibility/ui-design-and-styling.md |   2 +-
 .../ui/accessibility/web-accessibility.md     |  10 +-
 .../ui/adaptive-responsive/best-practices.md  |   8 +-
 .../ui/adaptive-responsive/capabilities.md    |  43 ++-
 src/content/ui/adaptive-responsive/general.md |   3 +-
 src/content/ui/adaptive-responsive/idioms.md  |   4 +-
 src/content/ui/adaptive-responsive/input.md   |   5 +-
 .../ui/adaptive-responsive/large-screens.md   |  11 +-
 .../safearea-mediaquery.md                    |   8 +-
 src/content/ui/animations/index.md            |   4 +-
 src/content/ui/animations/overview.md         |   2 +-
 src/content/ui/design/cupertino/index.md      |   1 -
 src/content/ui/design/graphics/index.md       |   2 +-
 src/content/ui/design/text/typography.md      |  38 +--
 src/content/ui/internationalization/index.md  |  23 +-
 src/content/ui/layout/constraints.md          |   4 +-
 src/content/ui/layout/tutorial.md             |   6 +-
 src/content/ui/navigation/index.md            |   2 +-
 267 files changed, 1624 insertions(+), 1676 deletions(-)

diff --git a/examples/ui/adaptive_app_demos/README.md b/examples/ui/adaptive_app_demos/README.md
index bc86e1e5423..9109cfe5794 100644
--- a/examples/ui/adaptive_app_demos/README.md
+++ b/examples/ui/adaptive_app_demos/README.md
@@ -1,7 +1,7 @@
 # adaptive_app_demos
 
 This project contains demo code for adaptive app development techniques from
-https://github.com/gskinnerTeam/flutter-adaptive-demo. 
+https://github.com/gskinnerTeam/flutter-adaptive-demo.
 
 Additional example code in this project was moved from
 code snippets originally seen in
@@ -9,4 +9,3 @@ code snippets originally seen in
 to ensure analysis in the flutter.dev CI pipeline.
 These snippets were intended to illustrate concepts and may
 therefore not be fully integrated/a functional part of the original demo code.
-
diff --git a/prompts/llmstxt_instructions.md b/prompts/llmstxt_instructions.md
index 9bfc40678ed..e0df2c861b1 100644
--- a/prompts/llmstxt_instructions.md
+++ b/prompts/llmstxt_instructions.md
@@ -60,4 +60,4 @@ If I encounter an error while performing an operation (for example, a tool fails
 
 ---
 
-By following these instructions, I will help keep the `llms.txt` file accurate and up-to-date.
\ No newline at end of file
+By following these instructions, I will help keep the `llms.txt` file accurate and up-to-date.
diff --git a/src/_includes/docs/china-notice-cn.md b/src/_includes/docs/china-notice-cn.md
index e7878ae54a2..2998c1fa5ab 100644
--- a/src/_includes/docs/china-notice-cn.md
+++ b/src/_includes/docs/china-notice-cn.md
@@ -3,4 +3,4 @@
 请参考 [在中国网络环境下使用 Flutter][] 文档.
 :::
 
-[在中国网络环境下使用 Flutter]: https://docs.flutter.cn/community/china
\ No newline at end of file
+[在中国网络环境下使用 Flutter]: https://docs.flutter.cn/community/china
diff --git a/src/_includes/docs/code-and-image.md b/src/_includes/docs/code-and-image.md
index 9f5df9ab3d1..47b7e0e071a 100644
--- a/src/_includes/docs/code-and-image.md
+++ b/src/_includes/docs/code-and-image.md
@@ -25,4 +25,3 @@
     {% endif -%}
   
 
-
diff --git a/src/_includes/docs/cookbook/networking/internet-permission.md b/src/_includes/docs/cookbook/networking/internet-permission.md
index 774a1499bd3..a71937ed26b 100644
--- a/src/_includes/docs/cookbook/networking/internet-permission.md
+++ b/src/_includes/docs/cookbook/networking/internet-permission.md
@@ -1,4 +1,4 @@
-If you are deploying to Android, edit your `AndroidManifest.xml` file to 
+If you are deploying to Android, edit your `AndroidManifest.xml` file to
 add the Internet permission.
 
 ```xml
@@ -6,7 +6,7 @@ add the Internet permission.
 
 ```
 
-Likewise, if you are deploying to macOS, edit your 
+Likewise, if you are deploying to macOS, edit your
 `macos/Runner/DebugProfile.entitlements` and `macos/Runner/Release.entitlements`
 files to include the network client entitlement.
 
diff --git a/src/_includes/docs/debug/debug-android-attach-process.md b/src/_includes/docs/debug/debug-android-attach-process.md
index ae686dab11a..bcf881a0fc2 100644
--- a/src/_includes/docs/debug/debug-android-attach-process.md
+++ b/src/_includes/docs/debug/debug-android-attach-process.md
@@ -50,9 +50,9 @@
 {% comment %}
    !['The Android Project view highlighting the GeneratedPluginRegistrant.java file.'](/assets/images/docs/testing/debugging/native/android-studio/debug-open-java-code.png){:width="100%"}
    
- + The Android Project view highlighting the `GeneratedPluginRegistrant.java` file. - +
{% endcomment %} @@ -77,4 +77,4 @@ The Dart debug pane with two breakpoints set in `lib/main.dart`. The Android debug pane with one breakpoint set in GeneratedPluginRegistrant.java. -{% endcomment %} \ No newline at end of file +{% endcomment %} diff --git a/src/_includes/docs/debug/debug-flow-xcode-as-start.md b/src/_includes/docs/debug/debug-flow-xcode-as-start.md index 357496e46b7..3827463bbc8 100644 --- a/src/_includes/docs/debug/debug-flow-xcode-as-start.md +++ b/src/_includes/docs/debug/debug-flow-xcode-as-start.md @@ -56,4 +56,4 @@ {% comment %} ![Alt text](/assets/images/docs/testing/debugging/vscode-ui/screens/vscode-add-attach-uri-filled.png) -{% endcomment %} \ No newline at end of file +{% endcomment %} diff --git a/src/_includes/docs/resource-links/ffi-video-resources.md b/src/_includes/docs/resource-links/ffi-video-resources.md index d9acf3489c7..ce5481a0fd9 100644 --- a/src/_includes/docs/resource-links/ffi-video-resources.md +++ b/src/_includes/docs/resource-links/ffi-video-resources.md @@ -7,4 +7,4 @@ To learn more about C interoperability, check out these videos: - [How to Use Dart FFI to Build a Retro Audio Player] [C interoperability with Dart FFI]: {{site.yt.watch}}?v=2MMK7YoFgaA -[How to Use Dart FFI to Build a Retro Audio Player]: {{site.yt.watch}}?v=05Wn2oM_nWw \ No newline at end of file +[How to Use Dart FFI to Build a Retro Audio Player]: {{site.yt.watch}}?v=05Wn2oM_nWw diff --git a/src/_includes/docs/swift-package-manager/how-to-enable-disable.md b/src/_includes/docs/swift-package-manager/how-to-enable-disable.md index 816905ab2bf..e401feabc66 100644 --- a/src/_includes/docs/swift-package-manager/how-to-enable-disable.md +++ b/src/_includes/docs/swift-package-manager/how-to-enable-disable.md @@ -71,7 +71,7 @@ flutter config --no-enable-swift-package-manager This turns off Swift Package Manager for the current user. If a project is incompatible with Swift Package Manager, all contributors -need to run this command. +need to run this command. [addSPM]: /packages-and-plugins/swift-package-manager/for-app-developers/#how-to-add-swift-package-manager-integration [removeSPM]: /packages-and-plugins/swift-package-manager/for-app-developers#how-to-remove-swift-package-manager-integration diff --git a/src/_includes/docs/swift-package-manager/migrate-objective-c-plugin.md b/src/_includes/docs/swift-package-manager/migrate-objective-c-plugin.md index dc11577f9ab..7eb5badaff1 100644 --- a/src/_includes/docs/swift-package-manager/migrate-objective-c-plugin.md +++ b/src/_includes/docs/swift-package-manager/migrate-objective-c-plugin.md @@ -41,9 +41,9 @@ The example below uses `ios`, replace `ios` with `macos`/`darwin` as applicable. ```swift title="Package.swift" // swift-tools-version: 5.9 // The swift-tools-version declares the minimum version of Swift required to build this package. - + import PackageDescription - + let package = Package( // TODO: Update your plugin name. name: "plugin_name", @@ -72,7 +72,7 @@ The example below uses `ios`, replace `ios` with `macos`/`darwin` as applicable. // For more information, see: // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files // .process("PrivacyInfo.xcprivacy"), - + // TODO: If you have other resources that need to be bundled with your plugin, refer to // the following instructions to add them: // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package @@ -128,7 +128,7 @@ The example below uses `ios`, replace `ios` with `macos`/`darwin` as applicable. // For more information, see: // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files // .process("PrivacyInfo.xcprivacy"), - + // TODO: If you have other resources that need to be bundled with your plugin, refer to // the following instructions to add them: // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package @@ -159,7 +159,7 @@ The example below uses `ios`, replace `ios` with `macos`/`darwin` as applicable. // For more information, see: // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files [!.process("PrivacyInfo.xcprivacy"),!] - + // TODO: If you have other resources that need to be bundled with your plugin, refer to // the following instructions to add them: // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package @@ -194,7 +194,7 @@ The example below uses `ios`, replace `ios` with `macos`/`darwin` as applicable. To remove the `modulemap` for Swift Package Manager but keep it for CocoaPods, exclude the `modulemap` and umbrella header in the plugin's `Package.swift` file. - + The example below assumes the `modulemap` and umbrella header are located in the `ios/plugin_name/Sources/plugin_name/include` directory. diff --git a/src/_includes/docs/swift-package-manager/migrate-swift-plugin.md b/src/_includes/docs/swift-package-manager/migrate-swift-plugin.md index 6185e2401e0..d60522e4ebf 100644 --- a/src/_includes/docs/swift-package-manager/migrate-swift-plugin.md +++ b/src/_includes/docs/swift-package-manager/migrate-swift-plugin.md @@ -34,9 +34,9 @@ The example below uses `ios`, replace `ios` with `macos`/`darwin` as applicable. ```swift title="Package.swift" // swift-tools-version: 5.9 // The swift-tools-version declares the minimum version of Swift required to build this package. - + import PackageDescription - + let package = Package( // TODO: Update your plugin name. name: "plugin_name", @@ -65,7 +65,7 @@ The example below uses `ios`, replace `ios` with `macos`/`darwin` as applicable. // For more information, see: // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files // .process("PrivacyInfo.xcprivacy"), - + // TODO: If you have other resources that need to be bundled with your plugin, refer to // the following instructions to add them: // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package @@ -117,7 +117,7 @@ The example below uses `ios`, replace `ios` with `macos`/`darwin` as applicable. // For more information, see: // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files // .process("PrivacyInfo.xcprivacy"), - + // TODO: If you have other resources that need to be bundled with your plugin, refer to // the following instructions to add them: // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package @@ -144,7 +144,7 @@ The example below uses `ios`, replace `ios` with `macos`/`darwin` as applicable. // For more information, see: // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files [!.process("PrivacyInfo.xcprivacy"),!] - + // TODO: If you have other resources that need to be bundled with your plugin, refer to // the following instructions to add them: // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package diff --git a/src/_includes/docs/test/integration/macos-example.md b/src/_includes/docs/test/integration/macos-example.md index b6dfa1169e4..33e5309c7ba 100644 --- a/src/_includes/docs/test/integration/macos-example.md +++ b/src/_includes/docs/test/integration/macos-example.md @@ -1,7 +1,7 @@ ```console $ flutter test integration_test -Resolving dependencies... -Downloading packages... +Resolving dependencies... +Downloading packages... flutter_lints 3.0.2 (4.0.0 available) > leak_tracker 10.0.4 (was 10.0.0) (10.0.5 available) > leak_tracker_flutter_testing 3.0.3 (was 2.0.1) (3.0.5 available) diff --git a/src/_includes/docs/tutorial/game-code.md b/src/_includes/docs/tutorial/game-code.md index 70baaf67c03..91803eaa9a0 100644 --- a/src/_includes/docs/tutorial/game-code.md +++ b/src/_includes/docs/tutorial/game-code.md @@ -1,14 +1,14 @@
- +
```dart diff --git a/src/_includes/docs/vscode-flutter-bar/hot-reload.md b/src/_includes/docs/vscode-flutter-bar/hot-reload.md index 5741cbad413..33786b5e6ed 100644 --- a/src/_includes/docs/vscode-flutter-bar/hot-reload.md +++ b/src/_includes/docs/vscode-flutter-bar/hot-reload.md @@ -1 +1 @@ -![Small yellow lightning bolt that indicates reloading the UI of a Flutter app without resetting any state values](/assets/images/docs/testing/debugging/vscode-ui/icons/hot-reload.png) \ No newline at end of file +![Small yellow lightning bolt that indicates reloading the UI of a Flutter app without resetting any state values](/assets/images/docs/testing/debugging/vscode-ui/icons/hot-reload.png) diff --git a/src/_includes/docs/vscode-flutter-bar/hot-restart.md b/src/_includes/docs/vscode-flutter-bar/hot-restart.md index ff89dd538b6..ece267dd14e 100644 --- a/src/_includes/docs/vscode-flutter-bar/hot-restart.md +++ b/src/_includes/docs/vscode-flutter-bar/hot-restart.md @@ -1 +1 @@ -![Small green almost circular arrow that indicates reloading the UI of a Flutter app and resetting any state values](/assets/images/docs/testing/debugging/vscode-ui/icons/hot-restart.png) \ No newline at end of file +![Small green almost circular arrow that indicates reloading the UI of a Flutter app and resetting any state values](/assets/images/docs/testing/debugging/vscode-ui/icons/hot-restart.png) diff --git a/src/_includes/docs/vscode-flutter-bar/inspector.md b/src/_includes/docs/vscode-flutter-bar/inspector.md index 58a9259ba5a..7a44518fc2c 100644 --- a/src/_includes/docs/vscode-flutter-bar/inspector.md +++ b/src/_includes/docs/vscode-flutter-bar/inspector.md @@ -1 +1 @@ -![Small blue magnifying class with the Flutter logo inside it that opens the Widget inspector](/assets/images/docs/testing/debugging/vscode-ui/icons/inspector.png) \ No newline at end of file +![Small blue magnifying class with the Flutter logo inside it that opens the Widget inspector](/assets/images/docs/testing/debugging/vscode-ui/icons/inspector.png) diff --git a/src/_includes/docs/vscode-flutter-bar/pause.md b/src/_includes/docs/vscode-flutter-bar/pause.md index c6d3545c6d6..f4e51c7a592 100644 --- a/src/_includes/docs/vscode-flutter-bar/pause.md +++ b/src/_includes/docs/vscode-flutter-bar/pause.md @@ -1 +1 @@ -![Small blue double vertical line that indicates pausing the Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/pause.png) \ No newline at end of file +![Small blue double vertical line that indicates pausing the Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/pause.png) diff --git a/src/_includes/docs/vscode-flutter-bar/play.md b/src/_includes/docs/vscode-flutter-bar/play.md index d12583a55ea..f9489ceeb74 100644 --- a/src/_includes/docs/vscode-flutter-bar/play.md +++ b/src/_includes/docs/vscode-flutter-bar/play.md @@ -1 +1 @@ -![Small blue vertical line with a blue triangle that indicates playing or resuming the Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/play-or-resume.png) \ No newline at end of file +![Small blue vertical line with a blue triangle that indicates playing or resuming the Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/play-or-resume.png) diff --git a/src/_includes/docs/vscode-flutter-bar/step-into.md b/src/_includes/docs/vscode-flutter-bar/step-into.md index 0a5e3ea4e9c..1dcd598d190 100644 --- a/src/_includes/docs/vscode-flutter-bar/step-into.md +++ b/src/_includes/docs/vscode-flutter-bar/step-into.md @@ -1 +1 @@ -![Small blue downward arrow over a blue circle that indicates going into the next function in a Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/step-into.png) \ No newline at end of file +![Small blue downward arrow over a blue circle that indicates going into the next function in a Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/step-into.png) diff --git a/src/_includes/docs/vscode-flutter-bar/step-out.md b/src/_includes/docs/vscode-flutter-bar/step-out.md index 3083a152a06..827bd97a26f 100644 --- a/src/_includes/docs/vscode-flutter-bar/step-out.md +++ b/src/_includes/docs/vscode-flutter-bar/step-out.md @@ -1 +1 @@ -![Small blue upward arrow over a blue circle that indicates exiting the current function after one passthrough in a Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/step-out.png) \ No newline at end of file +![Small blue upward arrow over a blue circle that indicates exiting the current function after one passthrough in a Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/step-out.png) diff --git a/src/_includes/docs/vscode-flutter-bar/step-over.md b/src/_includes/docs/vscode-flutter-bar/step-over.md index 9466e79886f..b0ac5b16b46 100644 --- a/src/_includes/docs/vscode-flutter-bar/step-over.md +++ b/src/_includes/docs/vscode-flutter-bar/step-over.md @@ -1 +1 @@ -![Small blue arched arrow over a blue circle that indicates skipping the current block or statement in the Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/step-over.png) \ No newline at end of file +![Small blue arched arrow over a blue circle that indicates skipping the current block or statement in the Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/step-over.png) diff --git a/src/_includes/docs/vscode-flutter-bar/stop.md b/src/_includes/docs/vscode-flutter-bar/stop.md index df7353db4f9..13e43a9b375 100644 --- a/src/_includes/docs/vscode-flutter-bar/stop.md +++ b/src/_includes/docs/vscode-flutter-bar/stop.md @@ -1 +1 @@ -![Red empty square that indicates you want to stop the running Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/stop.png) \ No newline at end of file +![Red empty square that indicates you want to stop the running Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/stop.png) diff --git a/src/content/add-to-app/android/plugin-setup.md b/src/content/add-to-app/android/plugin-setup.md index edf76a2e65b..f8109194e32 100644 --- a/src/content/add-to-app/android/plugin-setup.md +++ b/src/content/add-to-app/android/plugin-setup.md @@ -2,7 +2,7 @@ title: Manage plugins and dependencies in add-to-app shortTitle: Plugin setup description: > - Learn how to use plugins and share your + Learn how to use plugins and share your plugin's library dependencies with your existing app. --- @@ -24,7 +24,7 @@ In the simple cases: There are no additional steps needed. Your add-to-app module will work the same way as a full-Flutter app. -Whether you integrate using Android Studio, +Whether you integrate using Android Studio, Gradle subproject or AARs, transitive Android Gradle libraries are automatically bundled as needed into your outer existing app. @@ -148,4 +148,3 @@ existing app and Flutter module plugin. [firebase_crashlytics]: {{site.pub}}/packages/firebase_crashlytics [Gradle file]: {{site.github}}/firebase/flutterfire/blob/bdb95fcacf7cf077d162d2f267eee54a8b0be3bc/packages/firebase_crashlytics/android/build.gradle#L40 [resolves dependency version conflicts]: https://docs.gradle.org/current/userguide/dependency_resolution.html#sub:resolution-strategy - diff --git a/src/content/ai-toolkit/chat-client-sample.md b/src/content/ai-toolkit/chat-client-sample.md index 07a82e53ce2..911d4bb3027 100644 --- a/src/content/ai-toolkit/chat-client-sample.md +++ b/src/content/ai-toolkit/chat-client-sample.md @@ -42,4 +42,3 @@ TODO: If Mit agrees, move this to an official Flutter repo {% endcomment %} [AI Chat README]: {{site.github}}/csells/flutter_ai_chat - diff --git a/src/content/ai-toolkit/custom-llm-providers.md b/src/content/ai-toolkit/custom-llm-providers.md index 65492c7d4ff..b64c23c71e8 100644 --- a/src/content/ai-toolkit/custom-llm-providers.md +++ b/src/content/ai-toolkit/custom-llm-providers.md @@ -1,4 +1,4 @@ ---- +--- title: Custom LLM providers description: > How to integrate with other Flutter features. diff --git a/src/content/ai-toolkit/feature-integration.md b/src/content/ai-toolkit/feature-integration.md index 3f05949ca2a..bf7725a18c1 100644 --- a/src/content/ai-toolkit/feature-integration.md +++ b/src/content/ai-toolkit/feature-integration.md @@ -111,7 +111,7 @@ class ChatPage extends StatelessWidget { ``` To see a complete example of setting up suggestions for the user, -take a look at the [suggestions example][]. +take a look at the [suggestions example][]. [suggestions example]: {{site.github}}/flutter/ai/blob/main/example/lib/suggestions/suggestions.dart @@ -136,7 +136,7 @@ class _HomePageState extends State { apiKey: geminiApiKey, ..., systemInstruction: Content.system(''' -You are a helpful assistant that generates recipes based on the ingredients and +You are a helpful assistant that generates recipes based on the ingredients and instructions provided as well as my food preferences, which are as follows: ${Settings.foodPreferences.isEmpty ? 'I don\'t have any food preferences' : Settings.foodPreferences} @@ -301,7 +301,7 @@ class _HomePageState extends State { } ``` -The `_createProvider` method +The `_createProvider` method creates a new provider with the history from the previous provider _and_ the new user preferences. @@ -723,4 +723,4 @@ a provider dynamically or Retrieval Augmented Generation (RAG). To see this in action, check out the [logging example app][]. -[logging example app]: {{site.github}}/flutter/ai/blob/main/example/lib/logging/logging.dart +[logging example app]: {{site.github}}/flutter/ai/blob/main/example/lib/logging/logging.dart diff --git a/src/content/ai/ai-rules.md b/src/content/ai/ai-rules.md index d110f229e68..0a5cd6ce400 100644 --- a/src/content/ai/ai-rules.md +++ b/src/content/ai/ai-rules.md @@ -58,7 +58,7 @@ specific environment. To do so, follow these steps: 1. In an LLM like [Gemini][], attach the `rules.md` file that you downloaded in the last step. - + 1. Provide a prompt to reformat the file for your desired editor. diff --git a/src/content/ai/create-with-ai.md b/src/content/ai/create-with-ai.md index 7fa3640bff2..c36192fa109 100644 --- a/src/content/ai/create-with-ai.md +++ b/src/content/ai/create-with-ai.md @@ -25,9 +25,9 @@ fine-tune the AI's behavior and enforce project-specific best practices. ## Build AI-powered experiences with Flutter Using AI in your Flutter app unlocks new user experiences that allow your app to -support natural language understanding and content generation. +support natural language understanding and content generation. -To get started building AI-powered experiences in Flutter, check out these +To get started building AI-powered experiences in Flutter, check out these resources: * [Firebase AI Logic][] - The official Firebase SDK for using generative AI @@ -99,7 +99,7 @@ You can install it by running the following command: gemini extensions install https://github.com/gemini-cli-extensions/flutter ``` -To learn more, see the [blog post][flutter-extension-blog] or +To learn more, see the [blog post][flutter-extension-blog] or the [README][flutter-extension]. [flutter-extension]: {{site.github}}/gemini-cli-extensions/flutter diff --git a/src/content/app-architecture/case-study/testing.md b/src/content/app-architecture/case-study/testing.md index f936b73153a..6e3c6b91296 100644 --- a/src/content/app-architecture/case-study/testing.md +++ b/src/content/app-architecture/case-study/testing.md @@ -103,7 +103,7 @@ This class doesn't need to be faked. :::note The code also defines a `MockGoRouter`. The router is mocked using [`package:mocktail`][], -and is outside the scope of this case-study. +and is outside the scope of this case-study. You can find general testing guidance in [Flutter's testing documentation][]. ::: diff --git a/src/content/app-architecture/design-patterns/command.md b/src/content/app-architecture/design-patterns/command.md index ab0546bd12d..23d3237dc3a 100644 --- a/src/content/app-architecture/design-patterns/command.md +++ b/src/content/app-architecture/design-patterns/command.md @@ -11,11 +11,11 @@ order: 4 -[Model-View-ViewModel (MVVM)][] is a design pattern -that separates a feature of an application into three parts: +[Model-View-ViewModel (MVVM)][] is a design pattern +that separates a feature of an application into three parts: the model, the view model, and the view. Views and view models make up the UI layer of an application. -Repositories and services represent the data layer of an application, +Repositories and services represent the data layer of an application, or the model layer of MVVM. A command is a class that wraps a method @@ -27,19 +27,19 @@ You can also use them to display different UI states, like loading indicators when an action is running, or display an error dialog when an action failed. -View models can become very complex -as an application grows +View models can become very complex +as an application grows and features become bigger. Commands can help to simplify view models and reuse code. -In this guide, you will learn -how to use the command pattern +In this guide, you will learn +how to use the command pattern to improve your view models. ## Challenges when implementing view models -View model classes in Flutter are typically implemented +View model classes in Flutter are typically implemented by extending the [`ChangeNotifier`][] class. This allows view models to call `notifyListeners()` to refresh views when data is updated. @@ -51,8 +51,8 @@ class HomeViewModel extends ChangeNotifier { } ``` -View models contain a representation of the UI state, -including the data being displayed. +View models contain a representation of the UI state, +including the data being displayed. For example, this `HomeViewModel` exposes the `User` instance to the view. @@ -130,9 +130,9 @@ void load() { } ``` -Managing the state of an action can get complicated -if the view model contains multiple actions. -For example, adding an `edit()` action to the `HomeViewModel` +Managing the state of an action can get complicated +if the view model contains multiple actions. +For example, adding an `edit()` action to the `HomeViewModel` can lead the following outcome: @@ -158,21 +158,21 @@ class HomeViewModel extends ChangeNotifier { } ``` -Sharing the running state -between the `load()` and `edit()` actions might not always work, -because you might want to show a different UI component +Sharing the running state +between the `load()` and `edit()` actions might not always work, +because you might want to show a different UI component when the `load()` action runs than when the `edit()` action runs; you'll have the same problem with the `error` state. ### Triggering UI actions from view models View model classes can run into problems when -executing UI actions and the view model's state changes. +executing UI actions and the view model's state changes. -For example, you might want to show a `SnackBar` when an error occurs, +For example, you might want to show a `SnackBar` when an error occurs, or navigate to a different screen when an action completes. -To implement this, listen for changes in the view model, -and perform the action depending on the state. +To implement this, listen for changes in the view model, +and perform the action depending on the state. In the view: @@ -200,7 +200,7 @@ void _onViewModelChanged() { } ``` -You need to clear the error state each time you execute this action, +You need to clear the error state each time you execute this action, otherwise this action happens each time `notifyListeners()` is called. @@ -215,13 +215,13 @@ void _onViewModelChanged() { ## Command pattern -You might find yourself repeating the above code over and over, -implementing a different running state -for each action in every view model. -At that point, it makes sense to extract this code +You might find yourself repeating the above code over and over, +implementing a different running state +for each action in every view model. +At that point, it makes sense to extract this code into a reusable pattern called a _command_. -A command is a class that encapsulates a view model action, +A command is a class that encapsulates a view model action, and exposes the different states that an action can have. @@ -247,8 +247,8 @@ class Command extends ChangeNotifier { } ``` -In the view model, -instead of defining an action directly with a method, +In the view model, +instead of defining an action directly with a method, you create a command object: @@ -268,17 +268,17 @@ class HomeViewModel extends ChangeNotifier { } ``` -The previous `load()` method becomes `_load()`, -and instead the command `load` gets exposed to the `View`. -The previous `running` and `error` states can be removed, +The previous `load()` method becomes `_load()`, +and instead the command `load` gets exposed to the `View`. +The previous `running` and `error` states can be removed, as they are now part of the command. ### Executing a command -Instead of calling `viewModel.load()` to run the load action, +Instead of calling `viewModel.load()` to run the load action, now you call `viewModel.load.execute()`. -The `execute()` method can also be called from within the view model. +The `execute()` method can also be called from within the view model. The following line of code runs the `load` command when the view model is created. @@ -290,13 +290,13 @@ HomeViewModel() { ``` The `execute()` method sets the running state to `true` -and resets the `error` and `completed` states. -When the action finishes, -the `running` state changes to `false` +and resets the `error` and `completed` states. +When the action finishes, +the `running` state changes to `false` and the `completed` state to `true`. If the `running` state is `true`, -the command cannot begin executing again. +the command cannot begin executing again. This prevents users from triggering a command multiple times by pressing a button rapidly. @@ -354,11 +354,11 @@ class Command extends ChangeNotifier { ### Listening to the command state -The `Command` class extends from `ChangeNotifier`, +The `Command` class extends from `ChangeNotifier`, allowing Views to listen to its states. -In the `ListenableBuilder`, -instead of passing the view model to `ListenableBuilder.listenable`, +In the `ListenableBuilder`, +instead of passing the view model to `ListenableBuilder.listenable`, pass the command: @@ -432,7 +432,7 @@ body: ListenableBuilder( ), ``` -You can define multiple commands classes in a single view model, +You can define multiple commands classes in a single view model, simplifying its implementation and minimizing the amount of repeated code. @@ -462,7 +462,7 @@ class HomeViewModel2 extends ChangeNotifier { ### Extending the command pattern -The command pattern can be extended in multiple ways. +The command pattern can be extended in multiple ways. For example, to support a different number of arguments. @@ -493,19 +493,19 @@ class HomeViewModel extends ChangeNotifier { ## Putting it all together -In this guide, -you learned how to use the command design pattern -to improve the implementation of view models +In this guide, +you learned how to use the command design pattern +to improve the implementation of view models when using the MVVM design pattern. -Below, you can find the full `Command` class +Below, you can find the full `Command` class as implemented in the [Compass App example][] -for the Flutter architecture guidelines. -It also uses the [`Result` class][] +for the Flutter architecture guidelines. +It also uses the [`Result` class][] to determine if the action completed successfully or with an error. This implementation also includes two types of commands, -a `Command0`, for actions without parameters, +a `Command0`, for actions without parameters, and a `Command1`, for actions that take one parameter. :::note diff --git a/src/content/app-architecture/design-patterns/key-value-data.md b/src/content/app-architecture/design-patterns/key-value-data.md index c800bd8946c..b09dfd030d4 100644 --- a/src/content/app-architecture/design-patterns/key-value-data.md +++ b/src/content/app-architecture/design-patterns/key-value-data.md @@ -12,22 +12,22 @@ order: 1 Most Flutter applications, no matter how small or big they are, -require storing data on the user’s device at some point, such as API keys, +require storing data on the user’s device at some point, such as API keys, user preferences or data that should be available offline. -In this recipe, you will learn how to integrate persistent storage -for key-value data in a Flutter application -that uses the recommended [Flutter architecture design][]. -If you aren’t familiar with storing data to disk at all, -you can read the [Store key-value data on disk][] recipe. +In this recipe, you will learn how to integrate persistent storage +for key-value data in a Flutter application +that uses the recommended [Flutter architecture design][]. +If you aren’t familiar with storing data to disk at all, +you can read the [Store key-value data on disk][] recipe. -Key-value stores are often used for saving simple data, -such as app configuration, -and in this recipe you’ll use it to save Dark Mode preferences. -If you want to learn how to store complex data on a device, -you’ll likely want to use SQL. -In that case, take a look at the cookbook recipe -that follows this one called [Persistent storage architecture: SQL][]. +Key-value stores are often used for saving simple data, +such as app configuration, +and in this recipe you’ll use it to save Dark Mode preferences. +If you want to learn how to store complex data on a device, +you’ll likely want to use SQL. +In that case, take a look at the cookbook recipe +that follows this one called [Persistent storage architecture: SQL][]. ## Example application: App with theme selection @@ -37,10 +37,10 @@ a list of items, and a text field input at the bottom. ToDo application in light mode -In the `AppBar`, -a `Switch` allows users to change between dark and light theme modes. -This setting is applied immediately and it’s stored in the device -using a key-value data storage service. +In the `AppBar`, +a `Switch` allows users to change between dark and light theme modes. +This setting is applied immediately and it’s stored in the device +using a key-value data storage service. The setting is restored when the user starts the application again. @@ -102,25 +102,25 @@ class ThemeSwitch extends StatelessWidget { ``` The `ThemeSwitchViewModel` implements a view model -as described in the MVVM pattern. +as described in the MVVM pattern. This view model contains the state of the `ThemeSwitch` widget, represented by the boolean variable `_isDarkMode`. The view model uses the `ThemeRepository` to store and load the dark mode setting. -It contains two different command actions: +It contains two different command actions: `load`, which loads the dark mode setting from the repository, -and `toggle`, which switches the state between dark mode and light mode. +and `toggle`, which switches the state between dark mode and light mode. It exposes the state through the `isDarkMode` getter. -The `_load` method implements the `load` command. -This method calls `ThemeRepository.isDarkMode` +The `_load` method implements the `load` command. +This method calls `ThemeRepository.isDarkMode` to obtain the stored setting and calls `notifyListeners()` to refresh the UI. -The `_toggle` method implements the `toggle` command. -This method calls `ThemeRepository.setDarkMode` -to store the new dark mode setting. +The `_toggle` method implements the `toggle` command. +This method calls `ThemeRepository.setDarkMode` +to store the new dark mode setting. As well, it changes the local state of `_isDarkMode` then calls `notifyListeners()` to update the UI. @@ -165,26 +165,26 @@ class ThemeSwitchViewModel extends ChangeNotifier { ### Theme selection data layer -Following the architecture guidelines, -the data layer is split into two parts: +Following the architecture guidelines, +the data layer is split into two parts: the `ThemeRepository` and the `SharedPreferencesService`. -The `ThemeRepository` is the single source of truth -for all the theming configuration settings, +The `ThemeRepository` is the single source of truth +for all the theming configuration settings, and handles any possible errors coming from the service layer. -In this example, -the `ThemeRepository` also exposes the dark mode setting -through an observable `Stream`. -This allows other parts of the application +In this example, +the `ThemeRepository` also exposes the dark mode setting +through an observable `Stream`. +This allows other parts of the application to subscribe to changes in the dark mode setting. The `ThemeRepository` depends on `SharedPreferencesService`. -The repository obtains the stored value from the service, +The repository obtains the stored value from the service, and stores it when it changes. The `setDarkMode()` method passes the new value to the `StreamController`, -so that any component listening to the `observeDarkMode` stream +so that any component listening to the `observeDarkMode` stream @@ -223,14 +223,14 @@ class ThemeRepository { } ``` -The `SharedPreferencesService` wraps -the `SharedPreferences` plugin functionality, -and calls to the `setBool()` and `getBool()` methods -to store the dark mode setting, +The `SharedPreferencesService` wraps +the `SharedPreferences` plugin functionality, +and calls to the `setBool()` and `getBool()` methods +to store the dark mode setting, hiding this third-party dependency from the rest of the application :::note -A third-party dependency is a way to refer to packages and plugins +A third-party dependency is a way to refer to packages and plugins developed by other programmers outside of your organization. ::: @@ -253,9 +253,9 @@ class SharedPreferencesService { ## Putting it all together -In this example, -the `ThemeRepository` and `SharedPreferencesService` are created -in the `main()` method +In this example, +the `ThemeRepository` and `SharedPreferencesService` are created +in the `main()` method and passed to the `MainApp` as constructor argument dependency. @@ -271,8 +271,8 @@ void main() { } ``` -Then, when the `ThemeSwitch` is created, -also create `ThemeSwitchViewModel` +Then, when the `ThemeSwitch` is created, +also create `ThemeSwitchViewModel` and pass the `ThemeRepository` as dependency. @@ -282,8 +282,8 @@ ThemeSwitch( ), ``` -The example application also includes the `MainAppViewModel` class, -which listens to changes in the `ThemeRepository` +The example application also includes the `MainAppViewModel` class, +which listens to changes in the `ThemeRepository` and exposes the dark mode setting to the `MaterialApp` widget. diff --git a/src/content/app-architecture/design-patterns/offline-first.md b/src/content/app-architecture/design-patterns/offline-first.md index ec09111a78e..1cd16fec9c2 100644 --- a/src/content/app-architecture/design-patterns/offline-first.md +++ b/src/content/app-architecture/design-patterns/offline-first.md @@ -11,44 +11,44 @@ order: 3 -An offline-first application is an app capable of offering most -or all of its functionality while being disconnected from the internet. -Offline-first applications usually rely on stored data -to offer users temporary access to data +An offline-first application is an app capable of offering most +or all of its functionality while being disconnected from the internet. +Offline-first applications usually rely on stored data +to offer users temporary access to data that would otherwise only be available online. -Some offline-first applications combine local and remote data seamlessly, -while other applications inform the user -when the application is using cached data. -In the same way, -some applications synchronize data in the background -while others require the user to explicitly synchronize it. -It all depends on the application requirements and the functionality it offers, +Some offline-first applications combine local and remote data seamlessly, +while other applications inform the user +when the application is using cached data. +In the same way, +some applications synchronize data in the background +while others require the user to explicitly synchronize it. +It all depends on the application requirements and the functionality it offers, and it’s up to the developer to decide which implementation fits their needs. -In this guide, -you will learn how to implement different approaches -to offline-first applications in Flutter, +In this guide, +you will learn how to implement different approaches +to offline-first applications in Flutter, following the [Flutter Architecture guidelines][]. ## Offline-first architecture -As explained in the common architecture concepts guide, -repositories act as the single source of truth. -They are responsible for presenting local or remote data, -and should be the only place where data can be modified. -In offline-first applications, -repositories combine different local and remote data sources -to present data in a single access point, +As explained in the common architecture concepts guide, +repositories act as the single source of truth. +They are responsible for presenting local or remote data, +and should be the only place where data can be modified. +In offline-first applications, +repositories combine different local and remote data sources +to present data in a single access point, independently of the connectivity state of the device. -This example uses the `UserProfileRepository`, -a repository that allows you to obtain and store `UserProfile` objects +This example uses the `UserProfileRepository`, +a repository that allows you to obtain and store `UserProfile` objects with offline-first support. -The `UserProfileRepository` uses two different data services: -one works with remote data, -and the other works with a local database. +The `UserProfileRepository` uses two different data services: +one works with remote data, +and the other works with a local database. The API client,`ApiClientService`, connects to a remote service using HTTP REST calls. @@ -68,7 +68,7 @@ class ApiClientService { } ``` -The database service, `DatabaseService`, stores data using SQL, +The database service, `DatabaseService`, stores data using SQL, similar to the one found in the [Persistent Storage Architecture: SQL][] recipe. @@ -87,7 +87,7 @@ class DatabaseService { } ``` -This example also uses the `UserProfile` data class +This example also uses the `UserProfile` data class that has been created using the [`freezed`][] package. @@ -101,19 +101,19 @@ abstract class UserProfile with _$UserProfile { } ``` -In apps that have complex data, +In apps that have complex data, such as when the remote data contains more fields than the needed by the UI, you might want to have one data class for the API and database services, -and another for the UI. -For example, -`UserProfileLocal` for the database entity, -`UserProfileRemote` for the API response object, -and then `UserProfile` for the UI data model class. +and another for the UI. +For example, +`UserProfileLocal` for the database entity, +`UserProfileRemote` for the API response object, +and then `UserProfile` for the UI data model class. The `UserProfileRepository` would take care of converting from one to the other when necessary. -This example also includes the `UserProfileViewModel`, -a view model that uses the `UserProfileRepository` +This example also includes the `UserProfileViewModel`, +a view model that uses the `UserProfileRepository` to display the `UserProfile` on a widget. @@ -139,29 +139,29 @@ class UserProfileViewModel extends ChangeNotifier { ## Reading data -Reading data is a fundamental part of any application +Reading data is a fundamental part of any application that relies on remote API services. -In offline-first applications, -you want to ensure that the access to this data is as fast as possible, -and that it doesn’t depend on the device being online -to provide data to the user. +In offline-first applications, +you want to ensure that the access to this data is as fast as possible, +and that it doesn’t depend on the device being online +to provide data to the user. This is similar to the [Optimistic State design pattern][]. -In this section, -you will learn two different approaches, -one that uses the database as a fallback, +In this section, +you will learn two different approaches, +one that uses the database as a fallback, and one that combines local and remote data using a `Stream`. ### Using local data as a fallback -As a first approach, -you can implement offline support by having a fallback mechanism +As a first approach, +you can implement offline support by having a fallback mechanism for when the user is offline or a network call fails. -In this case, the `UserProfileRepository` attempts to obtain the `UserProfile` +In this case, the `UserProfileRepository` attempts to obtain the `UserProfile` from the remote API server using the `ApiClientService`. -If this request fails, +If this request fails, then returns the locally stored `UserProfile` from the `DatabaseService`. @@ -193,24 +193,24 @@ Future getUserProfile() async { ### Using a Stream -A better alternative presents the data using a `Stream`. -In the best case scenario, +A better alternative presents the data using a `Stream`. +In the best case scenario, the `Stream` emits two values, the locally stored data, and the data from the server. -First, the stream emits the locally stored data using the `DatabaseService`. -This call is generally faster and less error prone than a network call, +First, the stream emits the locally stored data using the `DatabaseService`. +This call is generally faster and less error prone than a network call, and by doing it first the view model can already display data to the user. -If the database does not contain any cached data, -then the `Stream` relies completely on the network call, +If the database does not contain any cached data, +then the `Stream` relies completely on the network call, emitting only one value. Then, the method performs the network call using the `ApiClientService` -to obtain up-to-date data. -If the request was successful, -it updates the database with the newly obtained data, -and then yields the value to the view model, +to obtain up-to-date data. +If the request was successful, +it updates the database with the newly obtained data, +and then yields the value to the view model, so it can be displayed to the user. @@ -236,11 +236,11 @@ Stream getUserProfile() async* { } ``` -The view model must subscribe -to this `Stream` and wait until it has completed. +The view model must subscribe +to this `Stream` and wait until it has completed. For that, call `asFuture()` with the `Subscription` object and await the result. -For each obtained value, +For each obtained value, update the view model data and call `notifyListeners()` so the UI shows the latest data. @@ -263,9 +263,9 @@ Future load() async { ``` ### Using only local data -Another possible approach uses locally stored data for read operations. -This approach requires that the data has been preloaded -at some point into the database, +Another possible approach uses locally stored data for read operations. +This approach requires that the data has been preloaded +at some point into the database, and requires a synchronization mechanism that can keep the data up to date. @@ -296,41 +296,41 @@ Future sync() async { } ``` -This approach can be useful for applications +This approach can be useful for applications that don’t require data to be in sync with the server at all times. -For example, a weather application +For example, a weather application where the weather data is only updated once a day. -Synchronization could be done manually by the user, -for example, a pull-to-refresh action that then calls the `sync()` method, -or done periodically by a `Timer` or a background process. -You can learn how to implement a synchronization task +Synchronization could be done manually by the user, +for example, a pull-to-refresh action that then calls the `sync()` method, +or done periodically by a `Timer` or a background process. +You can learn how to implement a synchronization task in the section about synchronizing state. ## Writing data -Writing data in offline-first applications depends fundamentally +Writing data in offline-first applications depends fundamentally on the application use case. -Some applications might require the user input data -to be immediately available on the server side, +Some applications might require the user input data +to be immediately available on the server side, while other applications might be more flexible and allow data to be out-of-sync temporarily. -This section explains two different approaches +This section explains two different approaches for implementing writing data in offline-first applications. ### Online-only writing -One approach for writing data in offline-first applications -is to enforce being online to write data. -While this might sound counterintuitive, -this ensures that the data the user has modified -is fully synchronized with the server, +One approach for writing data in offline-first applications +is to enforce being online to write data. +While this might sound counterintuitive, +this ensures that the data the user has modified +is fully synchronized with the server, and the application doesn’t have a different state than the server. -In this case, you first attempt to send the data to the API service, -and if the request succeeds, +In this case, you first attempt to send the data to the API service, +and if the request succeeds, then store the data in the database. @@ -349,15 +349,15 @@ Future updateUserProfile(UserProfile userProfile) async { } ``` -The disadvantage in this case is that the offline-first functionality -is only available for read operations, +The disadvantage in this case is that the offline-first functionality +is only available for read operations, but not for write operations, as those require the user being online. ### Offline-first writing -The second approach works the other way around. -Instead of performing the network call first, -the application first stores the new data in the database, +The second approach works the other way around. +Instead of performing the network call first, +the application first stores the new data in the database, and then attempts to send it to the API service once it has been stored locally. @@ -375,31 +375,31 @@ Future updateUserProfile(UserProfile userProfile) async { } ``` -This approach allows users to store data locally -even when the application is offline, -however, if the network call fails, -the local database and the API service are no longer in sync. -In the next section, -you will learn different approaches to handle synchronization +This approach allows users to store data locally +even when the application is offline, +however, if the network call fails, +the local database and the API service are no longer in sync. +In the next section, +you will learn different approaches to handle synchronization between local and remote data. ## Synchronizing state -Keeping the local and remote data in sync -is an important part of offline-first applications, -as the changes that have been done locally +Keeping the local and remote data in sync +is an important part of offline-first applications, +as the changes that have been done locally need to be copied to the remote service. -The app must also ensure that, when the user goes back to the application, +The app must also ensure that, when the user goes back to the application, the locally stored data is the same as in the remote service. ### Writing a synchronization task -There are different approaches for implementing +There are different approaches for implementing synchronization in a background task. -A simple solution is to create a `Timer` -in the `UserProfileRepository` that runs periodically, +A simple solution is to create a `Timer` +in the `UserProfileRepository` that runs periodically, for example every five minutes. @@ -407,7 +407,7 @@ for example every five minutes. Timer.periodic(const Duration(minutes: 5), (timer) => sync()); ``` -The `sync()` method then fetches the `UserProfile` from the database, +The `sync()` method then fetches the `UserProfile` from the database, and if it requires synchronization, it is then sent to the API service. @@ -435,35 +435,35 @@ Future sync() async { } ``` -A more complex solution uses background processes -like the [`workmanager`][] plugin. -This allows your application to run the synchronization process +A more complex solution uses background processes +like the [`workmanager`][] plugin. +This allows your application to run the synchronization process in the background even when the application is not running. :::note -Running background operations continuously -can drain the device battery dramatically, -and some devices limit the background processing capabilities, -so this approach needs to be tuned +Running background operations continuously +can drain the device battery dramatically, +and some devices limit the background processing capabilities, +so this approach needs to be tuned to the application requirements and one solution might not fit all cases. ::: -It’s also recommended to only perform the synchronization task +It’s also recommended to only perform the synchronization task when the network is available. -For example, you can use the [`connectivity_plus`][] plugin -to check if the device is connected to WiFi. -You can also use [`battery_plus`][] to verify +For example, you can use the [`connectivity_plus`][] plugin +to check if the device is connected to WiFi. +You can also use [`battery_plus`][] to verify that the device is not running low on battery. -In the previous example, the synchronization task runs every 5 minutes. -In some cases, that might be excessive, -while in others it might not be frequent enough. -The actual synchronization period time for your application +In the previous example, the synchronization task runs every 5 minutes. +In some cases, that might be excessive, +while in others it might not be frequent enough. +The actual synchronization period time for your application depends on your application needs and it’s something you will have to decide. ### Storing a synchronization flag -To know if the data requires synchronization, +To know if the data requires synchronization, add a flag to the data class indicating if the changes need to be synchronized. For example, `bool synchronized`: @@ -481,45 +481,45 @@ abstract class UserProfile with _$UserProfile { ``` Your synchronization logic should attempt -to send it to the API service +to send it to the API service only when the `synchronized` flag is `false`. If the request is successful, then change it to `true`. ### Pushing data from server -A different approach for synchronization -is to use a push service to provide up-to-date data to the application. -In this case, the server notifies the application when data has changed, +A different approach for synchronization +is to use a push service to provide up-to-date data to the application. +In this case, the server notifies the application when data has changed, instead of being the application asking for updates. -For example, you can use [Firebase messaging][], -to push small payloads of data to the device, +For example, you can use [Firebase messaging][], +to push small payloads of data to the device, as well as trigger synchronization tasks remotely using background messages. -Instead of having a synchronization task running in the background, -the server notifies the application +Instead of having a synchronization task running in the background, +the server notifies the application when the stored data needs to be updated with a push notification. -You can combine both approaches together, -having a background synchronization task and using background push messages, +You can combine both approaches together, +having a background synchronization task and using background push messages, to keep the application database synchronized with the server. ## Putting it all together -Writing an offline-first application -requires making decisions regarding -the way read, write and sync operations are implemented, +Writing an offline-first application +requires making decisions regarding +the way read, write and sync operations are implemented, which depend on the requirements from the application you are developing. The key takeaways are: -- When reading data, +- When reading data, you can use a `Stream` to combine locally stored data with remote data. -- When writing data, -decide if you need to be online or offline, +- When writing data, +decide if you need to be online or offline, and if you need synchronizing data later or not. -- When implementing a background sync task, -take into account the device status and your application needs, +- When implementing a background sync task, +take into account the device status and your application needs, as different applications may have different requirements. [Flutter Architecture guidelines]:/app-architecture diff --git a/src/content/app-architecture/design-patterns/optimistic-state.md b/src/content/app-architecture/design-patterns/optimistic-state.md index 852846c3302..f1204081b61 100644 --- a/src/content/app-architecture/design-patterns/optimistic-state.md +++ b/src/content/app-architecture/design-patterns/optimistic-state.md @@ -10,53 +10,53 @@ order: 0 -When building user experiences, -the perception of performance is sometimes just as important as -the actual performance of the code. -In general, users don’t like waiting for an action to finish to see the result, +When building user experiences, +the perception of performance is sometimes just as important as +the actual performance of the code. +In general, users don’t like waiting for an action to finish to see the result, and anything that takes more than a few milliseconds could be considered “slow” or “unresponsive” from the user’s perspective. -Developers can help mitigate this negative perception -by presenting a successful UI state -before the background task is fully completed. -An example of this would be tapping a “Subscribe” button, -and seeing it change to “Subscribed” instantly, +Developers can help mitigate this negative perception +by presenting a successful UI state +before the background task is fully completed. +An example of this would be tapping a “Subscribe” button, +and seeing it change to “Subscribed” instantly, even if the background call to the subscription API is still running. -This technique is known as Optimistic State, Optimistic UI or -Optimistic User Experience. -In this recipe, -you will implement an application feature using Optimistic State and +This technique is known as Optimistic State, Optimistic UI or +Optimistic User Experience. +In this recipe, +you will implement an application feature using Optimistic State and following the [Flutter architecture guidelines][]. ## Example feature: a subscribe button -This example implements a subscribe button similar to +This example implements a subscribe button similar to the one you could find in a video streaming application or a newsletter. Application with subscribe button -When the button is tapped, the application then calls an external API, -performing a subscription action, -for example recording in a database that the user is now in -the subscription list. -For demo purposes, you will not implement the actual backend code, -instead you will replace this call with +When the button is tapped, the application then calls an external API, +performing a subscription action, +for example recording in a database that the user is now in +the subscription list. +For demo purposes, you will not implement the actual backend code, +instead you will replace this call with a fake action that will simulate a network request. -In the case that the call is successful, -the button text will change from “Subscribe” to “Subscribed”. +In the case that the call is successful, +the button text will change from “Subscribe” to “Subscribed”. The button background color will change as well. -On the contrary, if the call fails, -the button text should revert back to “Subscribe”, -and the UI should show an error message to the user, +On the contrary, if the call fails, +the button text should revert back to “Subscribe”, +and the UI should show an error message to the user, for example using a Snackbar. -Following the Optimistic State idea, -the button should instantly change to “Subscribed” once it is tapped, +Following the Optimistic State idea, +the button should instantly change to “Subscribed” once it is tapped, and only change back to “Subscribe” if the request failed. Animation of application with subscribe butt
 
 ## Feature architecture
 
-Start by defining the feature architecture. 
-Following the architecture guidelines, 
+Start by defining the feature architecture.
+Following the architecture guidelines,
 create these Dart classes in a Flutter project:
 
 - A `StatefulWidget` named `SubscribeButton`
@@ -93,20 +93,20 @@ class SubscribeButtonViewModel extends ChangeNotifier {}
 class SubscriptionRepository {}
 ```
 
-The `SubscribeButton` widget and the `SubscribeButtonViewModel` represent 
-the presentation layer of this solution. 
-The widget is going to display a button 
-that will show the text “Subscribe” or “Subscribed” 
-depending on the subscription state. 
-The view model will contain the subscription state. 
-When the button is tapped, 
+The `SubscribeButton` widget and the `SubscribeButtonViewModel` represent
+the presentation layer of this solution.
+The widget is going to display a button
+that will show the text “Subscribe” or “Subscribed”
+depending on the subscription state.
+The view model will contain the subscription state.
+When the button is tapped,
 the widget will call the view model to perform the action.
 
-The `SubscriptionRepository` will implement a subscribe method 
-that will throw an exception when the action fails. 
-The view model will call this method when performing the subscription action. 
+The `SubscriptionRepository` will implement a subscribe method
+that will throw an exception when the action fails.
+The view model will call this method when performing the subscription action.
 
-Next, connect them together by adding the `SubscriptionRepository` 
+Next, connect them together by adding the `SubscriptionRepository`
 to the `SubscribeButtonViewModel`:
 
 <?code-excerpt @@ -133,7 +133,7 @@ class SubscribeButton extends StatefulWidget { } ``` -Now that you have created the basic solution architecture, +Now that you have created the basic solution architecture, you can create the `SubscribeButton` widget the following way: @@ -146,7 +146,7 @@ SubscribeButton( ``` ### Implement the `SubscriptionRepository` -Add a new asynchronous method named `subscribe()` +Add a new asynchronous method named `subscribe()` to the `SubscriptionRepository` with the following code: @@ -162,18 +162,18 @@ class SubscriptionRepository { } ``` -The call to `await Future.delayed()` with a duration of one second -has been added to simulate a long running request. +The call to `await Future.delayed()` with a duration of one second +has been added to simulate a long running request. The method execution will pause for a second, and then it will continue running. -In order to simulate a request failing, -the subscribe method throws an exception at the end. -This will be used later on to show how to recover from a failed request +In order to simulate a request failing, +the subscribe method throws an exception at the end. +This will be used later on to show how to recover from a failed request when implementing Optimistic State. ### Implement the `SubscribeButtonViewModel` -To represented the subscription state, as well a possible error state, +To represented the subscription state, as well a possible error state, add the following public members to the `SubscribeButtonViewModel`: @@ -187,13 +187,13 @@ bool error = false; Both are set to `false` on start. -Following the ideas of Optimistic State, -the `subscribed` state will change to `true` -as soon as the user taps the subscribe button. +Following the ideas of Optimistic State, +the `subscribed` state will change to `true` +as soon as the user taps the subscribe button. And will only change back to `false` if the action fails. -The `error` state will change to `true` when the action fails, -indicating the `SubscribeButton` widget to show an error message to the user. +The `error` state will change to `true` when the action fails, +indicating the `SubscribeButton` widget to show an error message to the user. The variable should go back to `false` once the error has been displayed. Next, implement an asynchronous `subscribe()` method: @@ -227,21 +227,21 @@ Future subscribe() async { } ``` -As described previously, first the method sets the `subscribed` state to `true` -and then calls to `notifyListeners()`. -This forces the UI to update and the button changes its appearance, +As described previously, first the method sets the `subscribed` state to `true` +and then calls to `notifyListeners()`. +This forces the UI to update and the button changes its appearance, showing the text “Subscribed” to the user. -Then the method performs the actual call to the repository. -This call is wrapped by a `try-catch` -in order to catch any exceptions it may throw. -In case an exception is caught, the `subscribed` state is set back to `false`, -and the `error` state is set to `true`. -A final call to `notifyListeners()` is done -to change the UI back to ‘Subscribe’. +Then the method performs the actual call to the repository. +This call is wrapped by a `try-catch` +in order to catch any exceptions it may throw. +In case an exception is caught, the `subscribed` state is set back to `false`, +and the `error` state is set to `true`. +A final call to `notifyListeners()` is done +to change the UI back to ‘Subscribe’. -If there is no exception, the process is complete -because the UI is already reflecting the success state. +If there is no exception, the process is complete +because the UI is already reflecting the success state. The complete `SubscribeButtonViewModel` should look like this: @@ -291,8 +291,8 @@ class SubscribeButtonViewModel extends ChangeNotifier { ### Implement the `SubscribeButton` -In this step, -you will first implement the build method of the `SubscribeButton`, +In this step, +you will first implement the build method of the `SubscribeButton`, and then implement the feature’s error handling. Add the following code to the build method: @@ -318,17 +318,17 @@ Widget build(BuildContext context) { } ``` -This build method contains a `ListenableBuilder` -that listens to changes from the view model. -The builder then creates a `FilledButton` -that will display the text "Subscribed" or "Subscribe" -depending on the view model state. -The button style will also change depending on this state. -As well, when the button is tapped, +This build method contains a `ListenableBuilder` +that listens to changes from the view model. +The builder then creates a `FilledButton` +that will display the text "Subscribed" or "Subscribe" +depending on the view model state. +The button style will also change depending on this state. +As well, when the button is tapped, it runs the `subscribe()` method from the view model. -The `SubscribeButtonStyle` can be found here. -Add this class next to the `SubscribeButton`. +The `SubscribeButtonStyle` can be found here. +Add this class next to the `SubscribeButton`. Feel free to modify the `ButtonStyle`. @@ -344,14 +344,14 @@ class SubscribeButtonStyle { } ``` -If you run the application now, -you will see how the button changes when you press it, +If you run the application now, +you will see how the button changes when you press it, however it will change back to the original state without showing an error. ### Handling errors -To handle errors, -add the `initState()` and `dispose()` methods to the `SubscribeButtonState`, +To handle errors, +add the `initState()` and `dispose()` methods to the `SubscribeButtonState`, and then add the `_onViewModelChange()` method. @@ -385,49 +385,49 @@ void _onViewModelChange() { } ``` -The `addListener()` call registers the `_onViewModelChange()` method -to be called when the view model notifies listeners. -It’s important to call `removeListener()` when the widget is disposed of, +The `addListener()` call registers the `_onViewModelChange()` method +to be called when the view model notifies listeners. +It’s important to call `removeListener()` when the widget is disposed of, in order to avoid errors. -The `_onViewModelChange()` method checks the `error` state, -and if it is `true`, -displays a `Snackbar` to the user showing an error message. -As well, the `error` state is set back to `false`, -to avoid displaying the error message multiple times +The `_onViewModelChange()` method checks the `error` state, +and if it is `true`, +displays a `Snackbar` to the user showing an error message. +As well, the `error` state is set back to `false`, +to avoid displaying the error message multiple times if `notifyListeners()` is called again in the view model. ## Advanced Optimistic State -In this tutorial, -you’ve learned how to implement an Optimistic State with a single binary state, -but you can use this technique to create a more advanced solution -by incorporating a third temporal state +In this tutorial, +you’ve learned how to implement an Optimistic State with a single binary state, +but you can use this technique to create a more advanced solution +by incorporating a third temporal state that indicates that the action is still running. -For example, in a chat application when the user sends a new message, -the application will display the new chat message in the chat window, -but with an icon indicating that the message is still pending to be delivered. +For example, in a chat application when the user sends a new message, +the application will display the new chat message in the chat window, +but with an icon indicating that the message is still pending to be delivered. When the message is delivered, that icon would be removed. -In the subscribe button example, -you could add another flag in the view model -indicating that the `subscribe()` method is still running, -or use the Command pattern running state, +In the subscribe button example, +you could add another flag in the view model +indicating that the `subscribe()` method is still running, +or use the Command pattern running state, then modify the button style slightly to show that the operation is running. ## Interactive example -This example shows the `SubscribeButton` widget -together with the `SubscribeButtonViewModel` -and `SubscriptionRepository`, +This example shows the `SubscribeButton` widget +together with the `SubscribeButtonViewModel` +and `SubscriptionRepository`, which implement a subscribe tap action with Optimistic State. -When you tap the button, -the button text changes from “Subscribe” to “Subscribed”. After a second, -the repository throws an exception, -which gets captured by the view model, -and the button reverts back to showing “Subscribe”, +When you tap the button, +the button text changes from “Subscribe” to “Subscribed”. After a second, +the repository throws an exception, +which gets captured by the view model, +and the button reverts back to showing “Subscribe”, while also displaying a Snackbar with an error message. diff --git a/src/content/app-architecture/design-patterns/result.md b/src/content/app-architecture/design-patterns/result.md index 955edd95657..18c503fc5b0 100644 --- a/src/content/app-architecture/design-patterns/result.md +++ b/src/content/app-architecture/design-patterns/result.md @@ -10,40 +10,40 @@ order: 5 -Dart provides a built-in error handling mechanism +Dart provides a built-in error handling mechanism with the ability to throw and catch exceptions. -As mentioned in the [Error handling documentation][], +As mentioned in the [Error handling documentation][], Dart's exceptions are unhandled exceptions. -This means that methods that throw exceptions don’t need to declare them, +This means that methods that throw exceptions don’t need to declare them, and calling methods aren't required to catch them either. -This can lead to situations where exceptions are not handled properly. -In large projects, -developers might forget to catch exceptions, -and the different application layers and components -could throw exceptions that aren’t documented. +This can lead to situations where exceptions are not handled properly. +In large projects, +developers might forget to catch exceptions, +and the different application layers and components +could throw exceptions that aren’t documented. This can lead to errors and crashes. -In this guide, -you will learn about this limitation +In this guide, +you will learn about this limitation and how to mitigate it using the _result_ pattern. ## Error flow in Flutter applications Applications following the [Flutter architecture guidelines][] -are usually composed of view models, -repositories, and services, among other parts. -When a function in one of these components fails, +are usually composed of view models, +repositories, and services, among other parts. +When a function in one of these components fails, it should communicate the error to the calling component. -Typically, that's done with exceptions. -For example, +Typically, that's done with exceptions. +For example, an API client service failing to communicate with the remote server -might throw an HTTP Error Exception. -The calling component, -for example a Repository, -would have to either capture this exception +might throw an HTTP Error Exception. +The calling component, +for example a Repository, +would have to either capture this exception or ignore it and let the calling view model handle it. This can be observed in the following example. Consider these classes: @@ -57,7 +57,7 @@ The `ApiClientService` contains a method, `getUserProfile`, that throws exceptions in certain situations: - The method throws an `HttpException` if the response code isn’t 200. -- The JSON parsing method throws an exception +- The JSON parsing method throws an exception if the response isn't formatted correctly. - The HTTP client might throw an exception due to networking issues. @@ -85,8 +85,8 @@ class ApiClientService { } ``` -The `UserProfileRepository` doesn’t need to handle -the exceptions from the `ApiClientService`. +The `UserProfileRepository` doesn’t need to handle +the exceptions from the `ApiClientService`. In this example, it just returns the value from the API Client. @@ -100,10 +100,10 @@ class UserProfileRepository { } ``` -Finally, the `UserProfileViewModel` +Finally, the `UserProfileViewModel` should capture all exceptions and handle the errors. -This can be done by wrapping +This can be done by wrapping the call to the `UserProfileRepository` with a try-catch: @@ -139,22 +139,22 @@ class UserProfileViewModel extends ChangeNotifier { } ``` -You can attempt to solve this by documenting the `ApiClientService`, -warning about the possible exceptions it might throw. -However, since the view model doesn’t use the service directly, +You can attempt to solve this by documenting the `ApiClientService`, +warning about the possible exceptions it might throw. +However, since the view model doesn’t use the service directly, other developers working in the codebase might miss this information. ## Using the result pattern -An alternative to throwing exceptions +An alternative to throwing exceptions is to wrap the function output in a `Result` object. -When the function runs successfully, -the `Result` contains the returned value. +When the function runs successfully, +the `Result` contains the returned value. However, if the function does not complete successfully, the `Result` object contains the error. -A `Result` is a [`sealed`][] class +A `Result` is a [`sealed`][] class that can either subclass `Ok` or the `Error` class. Return the successful value with the subclass `Ok`, and the captured error with the subclass `Error`. @@ -202,16 +202,16 @@ final class Error extends Result { ``` In this example, -the `Result` class uses a generic type `T` to represent any return value, +the `Result` class uses a generic type `T` to represent any return value, which can be a primitive Dart type like `String` or an `int` or a custom class like `UserProfile`. ### Creating a `Result` object -For functions using the `Result` class to return values, -instead of a value, +For functions using the `Result` class to return values, +instead of a value, the function returns a `Result` object containing the value. -For example, in the `ApiClientService`, +For example, in the `ApiClientService`, `getUserProfile` is changed to return a `Result`: @@ -225,16 +225,16 @@ class ApiClientService { } ``` -Instead of returning the `UserProfile` directly, +Instead of returning the `UserProfile` directly, it returns a `Result` object containing a `UserProfile`. -To facilitate using the `Result` class, -it contains two named constructors, `Result.ok` and `Result.error`. -Use them to construct the `Result` depending on desired output. -As well, capture any exceptions thrown by the code +To facilitate using the `Result` class, +it contains two named constructors, `Result.ok` and `Result.error`. +Use them to construct the `Result` depending on desired output. +As well, capture any exceptions thrown by the code and wrap them into the `Result` object. -For example, here the `getUserProfile()` method +For example, here the `getUserProfile()` method has been changed to use the `Result` class: @@ -261,17 +261,17 @@ class ApiClientService { } ``` -The original return statement was replaced -with a statement that returns the value using `Result.ok`. -The `throw HttpException()` +The original return statement was replaced +with a statement that returns the value using `Result.ok`. +The `throw HttpException()` was replaced with a statement that returns `Result.error(HttpException())`, -wrapping the error into a `Result`. +wrapping the error into a `Result`. As well, the method is wrapped with a `try-catch` block -to capture any exceptions thrown by the Http client +to capture any exceptions thrown by the Http client or the JSON parser into a `Result.error`. -The repository class also needs to be modified, -and instead of returning a `UserProfile` directly, +The repository class also needs to be modified, +and instead of returning a `UserProfile` directly, now it returns a `Result`. @@ -283,11 +283,11 @@ Future> getUserProfile() async { ### Unwrapping the Result object -Now the view model doesn't receive the `UserProfile` directly, +Now the view model doesn't receive the `UserProfile` directly, but instead it receives a `Result` containing a `UserProfile`. -This forces the developer implementing the view model -to unwrap the `Result` to obtain the `UserProfile`, +This forces the developer implementing the view model +to unwrap the `Result` to obtain the `UserProfile`, and avoids having uncaught exceptions. @@ -312,13 +312,13 @@ class UserProfileViewModel extends ChangeNotifier { } ``` -The `Result` class is implemented using a `sealed` class, -meaning it can only be of type `Ok` or `Error`. -This allows the code to evaluate the result with a -[switch result or expression][]. +The `Result` class is implemented using a `sealed` class, +meaning it can only be of type `Ok` or `Error`. +This allows the code to evaluate the result with a +[switch result or expression][]. -In the `Ok` case, -obtain the value using the `value` property. +In the `Ok` case, +obtain the value using the `value` property. In the `Error` case, obtain the error object using the `error` property. @@ -349,12 +349,12 @@ class UserProfileRepository { } ``` -In this method, the `UserProfileRepository` -attempts to obtain the `UserProfile` +In this method, the `UserProfileRepository` +attempts to obtain the `UserProfile` using the `ApiClientService`. If it fails, it tries to create a temporary user in a `DatabaseService`. -Because either service method can fail, +Because either service method can fail, the code must catch the exceptions in both cases. This can be improved using the `Result` pattern: @@ -377,29 +377,29 @@ Future> getUserProfile() async { } ``` -In this code, if the `Result` object is an `Ok` instance, -then the function returns that object; +In this code, if the `Result` object is an `Ok` instance, +then the function returns that object; otherwise, it returns `Result.Error`. ## Putting it all together -In this guide, you have learned +In this guide, you have learned how to use a `Result` class to return result values. The key takeaways are: -- `Result` classes force the calling method to check for errors, +- `Result` classes force the calling method to check for errors, reducing the amount of bugs caused by uncaught exceptions. - `Result` classes help improve control flow compared to try-catch blocks. -- `Result` classes are `sealed` and can only return `Ok` or `Error` instances, +- `Result` classes are `sealed` and can only return `Ok` or `Error` instances, allowing the code to unwrap them with a switch statement. -Below you can find the full `Result` class -as implemented in the [Compass App example][] +Below you can find the full `Result` class +as implemented in the [Compass App example][] for the [Flutter architecture guidelines][]. :::note -Check [pub.dev][] for different ready-to-use +Check [pub.dev][] for different ready-to-use implementations of the `Result` class, such as the [`result_dart`][], [`result_type`][], and [`multiple_result`][] packages. ::: diff --git a/src/content/app-architecture/design-patterns/sql.md b/src/content/app-architecture/design-patterns/sql.md index daac2603361..6aa4d528304 100644 --- a/src/content/app-architecture/design-patterns/sql.md +++ b/src/content/app-architecture/design-patterns/sql.md @@ -10,47 +10,47 @@ order: 2 -Most Flutter applications, -no matter how small or big they are, -might require storing data on the user’s device at some point. -For example, API keys, +Most Flutter applications, +no matter how small or big they are, +might require storing data on the user’s device at some point. +For example, API keys, user preferences or data that should be available offline. -In this recipe, -you will learn how to integrate persistent storage for complex data using SQL +In this recipe, +you will learn how to integrate persistent storage for complex data using SQL in a Flutter application following the Flutter Architecture design pattern. -To learn how to store simpler key-value data, -take a look at the Cookbook recipe: +To learn how to store simpler key-value data, +take a look at the Cookbook recipe: [Persistent storage architecture: Key-value data][]. -To read this recipe, -you should be familiar with SQL and SQLite. -If you need help, you can read the [Persist data with SQLite][] recipe +To read this recipe, +you should be familiar with SQL and SQLite. +If you need help, you can read the [Persist data with SQLite][] recipe before reading this one. -This example uses [`sqflite`][] with the [`sqflite_common_ffi`][] plugin, -which combined support for mobile and desktop. -Support for web is provided in the experimental plugin +This example uses [`sqflite`][] with the [`sqflite_common_ffi`][] plugin, +which combined support for mobile and desktop. +Support for web is provided in the experimental plugin [`sqflite_common_ffi_web`][] but it's not included in this example. ## Example application: ToDo list application -The example application consists of a single screen with an app bar at the top, +The example application consists of a single screen with an app bar at the top, a list of items, and a text field input at the bottom. ToDo application in light mode -The body of the application contains the `TodoListScreen`. +The body of the application contains the `TodoListScreen`. This screen contains a `ListView` of `ListTile` items, each one representing a ToDo item. -At the bottom, a `TextField` allows users to create new ToDo items +At the bottom, a `TextField` allows users to create new ToDo items by writing the task description and then tapping on the “Add” `FilledButton`. -Users can tap on the delete `IconButton` to delete the ToDo item. +Users can tap on the delete `IconButton` to delete the ToDo item. -The list of ToDo items is stored locally using a database service, +The list of ToDo items is stored locally using a database service, and restored when the user starts the application. :::note @@ -61,7 +61,7 @@ available in [`/examples/app-architecture/todo_data_service/`][]. ## Storing complex data with SQL This functionality follows the recommended [Flutter Architecture design][], -containing a UI layer and a data layer. +containing a UI layer and a data layer. Additionally, in the domain layer you will find the data model used. - UI layer with `TodoListScreen` and `TodoListViewModel` @@ -70,21 +70,21 @@ Additionally, in the domain layer you will find the data model used. ### ToDo list presentation layer -The `TodoListScreen` is a Widget that contains the UI in charge of displaying -and creating the ToDo items. -It follows the [MVVM pattern][] -and is accompanied by the `TodoListViewModel`, -which contains the list of ToDo items +The `TodoListScreen` is a Widget that contains the UI in charge of displaying +and creating the ToDo items. +It follows the [MVVM pattern][] +and is accompanied by the `TodoListViewModel`, +which contains the list of ToDo items and three commands to load, add, and delete ToDo items. -This screen is divided into two parts, -one containing the list of ToDo items, -implemented using a `ListView`, -and the other is a `TextField` +This screen is divided into two parts, +one containing the list of ToDo items, +implemented using a `ListView`, +and the other is a `TextField` and a `Button`, used for creating new ToDo items. -The `ListView` is wrapped by a `ListenableBuilder`, -which listens to changes in the `TodoListViewModel`, +The `ListView` is wrapped by a `ListenableBuilder`, +which listens to changes in the `TodoListViewModel`, and shows a `ListTile` for each ToDo item. @@ -109,8 +109,8 @@ ListenableBuilder( ) ``` -The list of ToDo items is defined in the `TodoListViewModel`, -and loaded by the `load` command. +The list of ToDo items is defined in the `TodoListViewModel`, +and loaded by the `load` command. This method calls the `TodoRepository` and fetches the list of ToDo items. @@ -151,14 +151,14 @@ FilledButton.icon( ) ``` -The `add` command then calls the `TodoRepository.createTodo()` method +The `add` command then calls the `TodoRepository.createTodo()` method with the task description text and it creates a new ToDo item. -The `createTodo()` method returns the newly created ToDo, +The `createTodo()` method returns the newly created ToDo, which is then added to the `_todo` list in the view model. -ToDo items contain a unique identifier generated by the database. -This is why the view model doesn’t create the ToDo item, +ToDo items contain a unique identifier generated by the database. +This is why the view model doesn’t create the ToDo item, but rather the `TodoRepository` does. @@ -205,8 +205,8 @@ IconButton( ) ``` -Then, the view model calls the `TodoRepository.deleteTodo()` method, -passing the unique ToDo item identifier. +Then, the view model calls the `TodoRepository.deleteTodo()` method, +passing the unique ToDo item identifier. A correct result removes the ToDo item from the view model *and* the screen. @@ -260,16 +260,16 @@ abstract class Todo with _$Todo { The data layer of this functionality is composed of two classes, the `TodoRepository` and the `DatabaseService`. -The `TodoRepository` acts as the source of truth for all the ToDo items. -View models must use this repository to access to the ToDo list, +The `TodoRepository` acts as the source of truth for all the ToDo items. +View models must use this repository to access to the ToDo list, and it should not expose any implementation details on how they are stored. -Internally, the `TodoRepository` uses the `DatabaseService`, +Internally, the `TodoRepository` uses the `DatabaseService`, which implements the access to the SQL database using the `sqflite` package. -You can implement the same `DatabaseService` using other storage packages +You can implement the same `DatabaseService` using other storage packages like `sqlite3`, `drift` or even cloud storage solutions like `firebase_database`. -The `TodoRepository` checks if the database is open +The `TodoRepository` checks if the database is open before every request and opens it if necessary. It implements the `fetchTodos()`, `createTodo()`, and `deleteTodo()` methods. @@ -304,10 +304,10 @@ class TodoRepository { } ``` -The `DatabaseService` implements the access to the SQLite database +The `DatabaseService` implements the access to the SQLite database using the `sqflite` package. -It’s a good idea to define the table and column names as constants +It’s a good idea to define the table and column names as constants to avoid typos when writing SQL code. @@ -317,7 +317,7 @@ static const String _idColumnName = '_id'; static const String _taskColumnName = 'task'; ``` -The `open()` method opens the existing database, +The `open()` method opens the existing database, or creates a new one if it doesn’t exist. @@ -338,11 +338,11 @@ Future open() async { ``` Note that the column `id` is set as `primary key` and `autoincrement`; -this means that each newly inserted item +this means that each newly inserted item is assigned a new value for the `id` column. -The `insert()` method creates a new ToDo item in the database, -and returns a newly created Todo instance. +The `insert()` method creates a new ToDo item in the database, +and returns a newly created Todo instance. The `id` is generated as mentioned before. @@ -360,10 +360,10 @@ Future> insert(String task) async { ``` All the `DatabaseService` operations use the `Result` class to return a value, -as recommended by the [Flutter architecture recommendations][]. +as recommended by the [Flutter architecture recommendations][]. This facilitates handling errors in further steps in the application code. -The `getAll()` method performs a database query, +The `getAll()` method performs a database query, obtaining all the values in the `id` and `task` columns. For each entry, it creates a `Todo` class instance. @@ -390,10 +390,10 @@ Future>> getAll() async { } ``` -The `delete()` method performs a database delete operation +The `delete()` method performs a database delete operation based on the ToDo item `id`. -In this case, if no items were deleted an error is returned, +In this case, if no items were deleted an error is returned, indicating that something went wrong. @@ -416,22 +416,22 @@ Future> delete(int id) async { ``` :::note -In some cases, you might want to close the database when you are done with it. -For example, when the user leaves the screen, -or after a certain time has passed. +In some cases, you might want to close the database when you are done with it. +For example, when the user leaves the screen, +or after a certain time has passed. -This depends on the database implementation -as well as your application requirements. -It’s recommended that you check with the database package authors +This depends on the database implementation +as well as your application requirements. +It’s recommended that you check with the database package authors for recommendations. ::: ## Putting it all together -In the `main()` method of your application, -first initialize the `DatabaseService`, -which requires different initialization code on different platforms. -Then, pass the newly created `DatabaseService` into the `TodoRepository` +In the `main()` method of your application, +first initialize the `DatabaseService`, +which requires different initialization code on different platforms. +Then, pass the newly created `DatabaseService` into the `TodoRepository` which is itself passed into the `MainApp` as a constructor argument dependency. @@ -458,8 +458,8 @@ void main() { } ``` -Then, when the `TodoListScreen` is created, -also create the `TodoListViewModel` +Then, when the `TodoListScreen` is created, +also create the `TodoListViewModel` and pass the `TodoRepository` to it as dependency. diff --git a/src/content/brand/index.md b/src/content/brand/index.md index f607ac59124..51979285875 100644 --- a/src/content/brand/index.md +++ b/src/content/brand/index.md @@ -123,4 +123,3 @@ below are specific exceptions to the rules described above: of a community event (e.g. conference), but please make sure to include the following disclaimer on the event website in a prominent and easy-to-see spot: "Flutter and the related logo are trademarks of Google LLC. [Title of event] is not affiliated with or otherwise sponsored by Google LLC." - diff --git a/src/content/community/china/index.md b/src/content/community/china/index.md index 73514223d69..0384fe27c75 100644 --- a/src/content/community/china/index.md +++ b/src/content/community/china/index.md @@ -43,7 +43,7 @@ These steps require using PowerShell. ```ps $ $env:PUB_HOSTED_URL="https://pub.flutter-io.cn" ``` - + 1. Set `FLUTTER_STORAGE_BASE_URL` to your mirror site. ```ps @@ -54,7 +54,7 @@ These steps require using PowerShell. For CFUG, visit their [Flutter SDK archive][], and download the SDK for your platform and architecture. - + 1. Create a folder where you can install Flutter. Then change into it. Consider a path like `$env:USERPROFILE\dev`. @@ -307,16 +307,16 @@ To enable access to `pub.dev`: 1. Configure a proxy. To configure a proxy, check out the [Dart documentation on proxies][]. - + 1. Verify that your `PUB_HOSTED_URL` environment variable is either unset or empty. - + ```ps $ echo $env:PUB_HOSTED_URL ``` - + If this command returns any value, unset it. - + ```ps $ Remove-Item $env:PUB_HOSTED_URL ``` @@ -330,13 +330,13 @@ To enable access to `pub.dev`: 1. Verify that your `PUB_HOSTED_URL` environment variable is either unset or empty. - + ```console $ echo $PUB_HOSTED_URL ``` - + If this command returns any value, unset it. - + ```console $ unset $PUB_HOSTED_URL ``` @@ -347,16 +347,16 @@ To enable access to `pub.dev`: 1. Configure a proxy. To configure a proxy, check out the [Dart documentation on proxies][]. - + 1. Verify that your `PUB_HOSTED_URL` environment variable is either unset or empty. - + ```console $ echo $PUB_HOSTED_URL ``` - + If this command returns any value, unset it. - + ```console $ unset $PUB_HOSTED_URL ``` diff --git a/src/content/contribute/docs/sidenav.md b/src/content/contribute/docs/sidenav.md index 7e498303360..88990b77950 100644 --- a/src/content/contribute/docs/sidenav.md +++ b/src/content/contribute/docs/sidenav.md @@ -27,5 +27,3 @@ the `/src/_data/sidenav.yml` file in [YAML][] format. ## Hide pages unless open ## Infrastructure - - diff --git a/src/content/cookbook/design/fonts.md b/src/content/cookbook/design/fonts.md index bb7d4da4ff4..0a150e444b1 100644 --- a/src/content/cookbook/design/fonts.md +++ b/src/content/cookbook/design/fonts.md @@ -31,7 +31,7 @@ This recipe creates an app that uses custom fonts with the following steps. 1. Set a font as the default. 1. Use a font in a specific widget. -You don't need to follow each step as you go. +You don't need to follow each step as you go. The guide offers completed example files at the end. :::note @@ -71,7 +71,7 @@ Flutter does not support fonts in the Web Open Font Format, #### Choose fonts for their specific benefits Few sources agree on what a font file type is or which uses less space. -The key difference between font file types involves how the format +The key difference between font file types involves how the format encodes the glyphs in the file. Most TrueType and OpenType font files have similar capabilities as they borrowed from each other as the formats and fonts improved over time. @@ -195,14 +195,14 @@ Flutter includes these files in the app's asset bundle. Different typefaces implement font files in different ways. If you need a typeface with a variety of font weights and styles, -choose and import font files that represent that variety. +choose and import font files that represent that variety. When you import a font file that doesn't include either multiple fonts within it or variable font capabilities, don't use the `style` or `weight` property to adjust how they display. If you do use those properties on a regular font file, Flutter attempts to _simulate_ the look. -The visual result will look quite different from using the correct font file. +The visual result will look quite different from using the correct font file. ### Set styles and weights with font files @@ -218,7 +218,7 @@ These values correspond to the [`FontWeight`][] and can be used in the In the `pubspec.yaml` shown in this guide, you defined `RobotoMono-Bold` as the `700` weight of the font family. -To use the `RobotoMono-Bold` font that you added to your app, +To use the `RobotoMono-Bold` font that you added to your app, set `fontWeight` to `FontWeight.w700` in your `TextStyle` widget. If you hadn't added `RobotoMono-Bold` to your app, @@ -239,8 +239,8 @@ You can use these styles in the [`fontStyle`][fontStyle property] property of a [`TextStyle`][] object. In the `pubspec.yaml` shown in this guide, -you defined `Raleway-Italic` as being in the `italic` style. -To use the `Raleway-Italic` font that you added to your app, +you defined `Raleway-Italic` as being in the `italic` style. +To use the `Raleway-Italic` font that you added to your app, set `style: TextStyle(fontStyle: FontStyle.italic)`. Flutter swaps `Raleway-Regular` with `Raleway-Italic` when rendering. @@ -282,7 +282,7 @@ check out the [Using Themes to share colors and font styles][] recipe. To apply the font to a specific widget like a `Text` widget, provide a [`TextStyle`][] to the widget. -For this guide, +For this guide, try to apply the `RobotoMono` font to a single `Text` widget. Match the `fontFamily` value to the `family` name declared in the `pubspec.yaml` file. @@ -324,15 +324,15 @@ Download the Raleway and RobotoMono font files from [Google Fonts][]. ```yaml name: custom_fonts description: An example of how to use custom fonts with Flutter - + dependencies: flutter: sdk: flutter - + dev_dependencies: flutter_test: sdk: flutter - + flutter: fonts: - family: Raleway @@ -361,12 +361,12 @@ Download the Raleway and RobotoMono font files from [Google Fonts][]. ```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( @@ -377,10 +377,10 @@ Download the Raleway and RobotoMono font files from [Google Fonts][]. ); } } - + class MyHomePage extends StatelessWidget { const MyHomePage({super.key}); - + @override Widget build(BuildContext context) { return Scaffold( diff --git a/src/content/cookbook/design/orientation.md b/src/content/cookbook/design/orientation.md index ce4f5c28345..f1f7580af9f 100644 --- a/src/content/cookbook/design/orientation.md +++ b/src/content/cookbook/design/orientation.md @@ -137,10 +137,10 @@ class OrientationList extends StatelessWidget { ## Locking device orientation -In the previous section, you learned +In the previous section, you learned how to adapt the app UI to device orientation changes. -Flutter also allows you to specify the orientations your app supports +Flutter also allows you to specify the orientations your app supports using the values of [`DeviceOrientation`]. You can either: - Lock the app to a single orientation, like only the `portraitUp` position, or... @@ -150,7 +150,7 @@ In the application `main()` method, call [`SystemChrome.setPreferredOrientations()`] with the list of preferred orientations that your app supports. -To lock the device to a single orientation, +To lock the device to a single orientation, you can pass a list with a single item. For a list of all the possible values, check out [`DeviceOrientation`]. diff --git a/src/content/cookbook/effects/download-button.md b/src/content/cookbook/effects/download-button.md index 12d406528b0..f8e4a5c7ad8 100644 --- a/src/content/cookbook/effects/download-button.md +++ b/src/content/cookbook/effects/download-button.md @@ -8,7 +8,7 @@ description: How to implement a download button. Apps are filled with buttons that execute long-running behaviors. For example, a button might trigger a download, which starts a download process, receives data over time, -and then provides access to the downloaded asset. +and then provides access to the downloaded asset. It's helpful to show the user the progress of a long-running process, and the button itself is a good place to provide this feedback. In this recipe, @@ -23,7 +23,7 @@ The following animation shows the app's behavior: Your button widget needs to change its appearance over time. Therefore, you need to implement your button with a custom -stateless widget. +stateless widget. Define a new stateless widget called `DownloadButton`. @@ -74,20 +74,20 @@ class DownloadButton extends StatelessWidget { :::note Each time you define a custom widget, -you must decide whether all relevant +you must decide whether all relevant information is provided to that widget from its parent or if that widget orchestrates the application behavior within itself. For example, `DownloadButton` could receive the -current `DownloadStatus` from its parent, +current `DownloadStatus` from its parent, or the `DownloadButton` could orchestrate the download process itself within its `State` object. -For most widgets, the best answer is to pass the relevant +For most widgets, the best answer is to pass the relevant information into the widget from its parent, rather than manage behavior within the widget. By passing in all the relevant information, you ensure greater reusability for the widget, -easier testing, and easier changes to application +easier testing, and easier changes to application behavior in the future. ::: @@ -97,16 +97,16 @@ The download button changes its shape based on the download status. The button displays a grey, rounded rectangle during the `notDownloaded` and `downloaded` states. The button displays a transparent circle during the -`fetchingDownload` and `downloading` states. +`fetchingDownload` and `downloading` states. Based on the current `DownloadStatus`, -build an `AnimatedContainer` with a +build an `AnimatedContainer` with a `ShapeDecoration` that displays a rounded rectangle or a circle. -Consider defining the shape's widget tree in a separated +Consider defining the shape's widget tree in a separated `Stateless` widget so that the main `build()` -method remains simple, allowing for the additions +method remains simple, allowing for the additions that follow. Instead of creating a function to return a widget, like `Widget _buildSomething() {}`, always prefer creating a `StatelessWidget` or a `StatefulWidget` which is more performant. More @@ -189,12 +189,12 @@ class ButtonShapeWidget extends StatelessWidget { You might wonder why you need a `ShapeDecoration` widget for a transparent circle, given that it's invisible. The purpose of the invisible circle is to orchestrate -the desired animation. The `AnimatedContainer` begins with a rounded +the desired animation. The `AnimatedContainer` begins with a rounded rectangle. When the `DownloadStatus` changes to `fetchingDownload`, the `AnimatedContainer` needs to animate from a rounded rectangle to a circle, and then fade out as the animation takes place. The only way to implement this animation is to define both -the beginning shape of a rounded rectangle and the +the beginning shape of a rounded rectangle and the ending shape of a circle. But, you don't want the final circle to be visible, so you make it transparent, which causes an animated fade-out. @@ -203,7 +203,7 @@ which causes an animated fade-out. The `DownloadButton` displays `GET` during the `notDownloaded` phase, `OPEN` during the `downloaded` -phase, and no text in between. +phase, and no text in between. Add widgets to display text during each download phase, and animate the text's opacity in between. Add the text @@ -272,8 +272,8 @@ class ButtonShapeWidget extends StatelessWidget { During the `fetchingDownload` phase, the `DownloadButton` displays a radial spinner. This spinner fades in from -the `notDownloaded` phase and fades out to -the `fetchingDownload` phase. +the `notDownloaded` phase and fades out to +the `fetchingDownload` phase. Implement a radial spinner that sits on top of the button shape and fades in and out at the appropriate times. @@ -318,12 +318,12 @@ Widget build(BuildContext context) { After the `fetchingDownload` phase is the `downloading` phase. During the `downloading` phase, the `DownloadButton` replaces the radial progress spinner with a growing -radial progress bar. The `DownloadButton` also displays a stop +radial progress bar. The `DownloadButton` also displays a stop button icon so that the user can cancel an in-progress download. Add a progress property to the `DownloadButton` widget, and then update the progress display to switch to a radial -progress bar during the `downloading` phase. +progress bar during the `downloading` phase. Next, add a stop button icon at the center of the radial progress bar. @@ -374,10 +374,10 @@ Widget build(BuildContext context) { ## Add button tap callbacks The last detail that your `DownloadButton` needs is the -button behavior. The button must do things when the user taps it. +button behavior. The button must do things when the user taps it. Add widget properties for callbacks to start a download, -cancel a download, and open a download. +cancel a download, and open a download. Finally, wrap `DownloadButton`'s existing widget tree with a `GestureDetector` widget, and forward the @@ -441,7 +441,7 @@ class DownloadButton extends StatelessWidget { Congratulations! You have a button that changes its display depending on which phase the button is in: not downloaded, fetching download, downloading, and downloaded. -Now, the user can tap to start a download, tap to cancel an +Now, the user can tap to start a download, tap to cancel an in-progress download, and tap to open a completed download. ## Interactive example diff --git a/src/content/cookbook/effects/drag-a-widget.md b/src/content/cookbook/effects/drag-a-widget.md index 0a285268688..f3097db275f 100644 --- a/src/content/cookbook/effects/drag-a-widget.md +++ b/src/content/cookbook/effects/drag-a-widget.md @@ -29,10 +29,10 @@ and display a draggable photo of a menu item. Flutter provides a widget called [`LongPressDraggable`][] that provides the exact behavior that you need to begin a drag-and-drop interaction. A `LongPressDraggable` -widget recognizes when a long press occurs and then +widget recognizes when a long press occurs and then displays a new widget near the user's finger. As the user drags, the widget follows the user's finger. -`LongPressDraggable` gives you full control over the +`LongPressDraggable` gives you full control over the widget that the user drags. Each menu list item is displayed with a custom @@ -70,21 +70,21 @@ In this case, when the user long presses on the `MenuListItem` widget, the `LongPressDraggable` widget displays a `DraggingListItem`. This `DraggingListItem` displays a photo of the -selected food item, centered beneath +selected food item, centered beneath the user's finger. The `dragAnchorStrategy` property is set to [`pointerDragAnchorStrategy`][]. This property value instructs `LongPressDraggable` -to base the `DraggableListItem`'s position on the +to base the `DraggableListItem`'s position on the user's finger. As the user moves a finger, the `DraggableListItem` moves with it. Dragging and dropping is of little use if no information is transmitted when the item is dropped. -For this reason, `LongPressDraggable` takes a `data` parameter. +For this reason, `LongPressDraggable` takes a `data` parameter. In this case, the type of `data` is `Item`, -which holds information about the +which holds information about the food menu item that the user pressed on. The `data` associated with a `LongPressDraggable` @@ -97,7 +97,7 @@ You'll implement the drop behavior next. The user can drop a `LongPressDraggable` wherever they choose, but dropping the draggable has no effect unless it's dropped on top of a `DragTarget`. When the user drops a draggable on -top of a `DragTarget` widget, the `DragTarget` widget +top of a `DragTarget` widget, the `DragTarget` widget can either accept or reject the data from the draggable. In this recipe, the user should drop a menu item on a @@ -140,20 +140,20 @@ When the user drags a draggable on the `DragTarget` widget, `candidateItems` contains the data items that the user is dragging. This draggable allows you to change what your widget looks like when the user is dragging over it. In this case, -the `Customer` widget turns red whenever any items are dragged above the -`DragTarget` widget. The red visual appearance is configured with the +the `Customer` widget turns red whenever any items are dragged above the +`DragTarget` widget. The red visual appearance is configured with the `highlighted` property within the `CustomerCart` widget. When the user drops a draggable on the `DragTarget` widget, the `onAcceptWithDetails` callback is invoked. This is when you get to decide whether or not to accept the data that was dropped. -In this case, the item is always accepted and processed. +In this case, the item is always accepted and processed. You might choose to inspect the incoming item to make a -different decision. +different decision. Notice that the type of item dropped on `DragTarget` must match the type of the item dragged from `LongPressDraggable`. -If the types are not compatible, then +If the types are not compatible, then the `onAcceptWithDetails` method isn't invoked. With a `DragTarget` widget configured to accept your @@ -208,7 +208,7 @@ void _itemDroppedOnCustomerCart({ The `_itemDroppedOnCustomerCart` method is invoked in `onAcceptWithDetails()` when the user drops a menu item on a -`CustomerCart` widget. By adding the dropped item to the +`CustomerCart` widget. By adding the dropped item to the `customer` object, and invoking `setState()` to cause a layout update, the UI refreshes with the new customer's price total and item count. diff --git a/src/content/cookbook/effects/expandable-fab.md b/src/content/cookbook/effects/expandable-fab.md index 056a4e03e23..44c87e23804 100644 --- a/src/content/cookbook/effects/expandable-fab.md +++ b/src/content/cookbook/effects/expandable-fab.md @@ -60,7 +60,7 @@ class _ExpandableFabState extends State { The `ExpandableFab` displays a blue edit button when collapsed and a white close button when expanded. When expanding and collapsing, -these two buttons scale and fade between one another. +these two buttons scale and fade between one another. Implement the expand and collapse cross-fade between the two different FABs. diff --git a/src/content/cookbook/effects/nested-nav.md b/src/content/cookbook/effects/nested-nav.md index d9599c09239..34643aaaedf 100644 --- a/src/content/cookbook/effects/nested-nav.md +++ b/src/content/cookbook/effects/nested-nav.md @@ -8,15 +8,15 @@ description: How to implement a flow with nested navigation. Apps accumulate dozens and then hundreds of routes over time. Some of your routes make sense as top-level (global) routes. For example, "/", "profile", "contact", "social_feed" are all -possible top-level routes within your app. +possible top-level routes within your app. But, imagine that you defined every possible route in your top-level `Navigator` widget. The list would be very long, -and many of these routes would +and many of these routes would be better handled nested within another widget. Consider an Internet of Things (IoT) setup flow for a wireless light bulb that you control with your app. -This setup flow consists of four pages: +This setup flow consists of four pages: * `find_devices` page: Find nearby bulbs. * `select_device` page: Select the bulb that you want to @@ -24,12 +24,12 @@ This setup flow consists of four pages: * `connecting` page: Add the bulb. * `finished` page: Complete the setup. -You could orchestrate this behavior from your top-level -`Navigator` widget. However, it makes more sense to define a second, +You could orchestrate this behavior from your top-level +`Navigator` widget. However, it makes more sense to define a second, nested `Navigator` widget within your `SetupFlow` widget, and let the nested `Navigator` take ownership over the four pages in the setup flow. This delegation of navigation facilitates -greater local control, which is +greater local control, which is generally preferable when developing software. The following animation shows the app's behavior: @@ -43,7 +43,7 @@ the top-level `Navigator` widget. ## Prepare for navigation This IoT app has two top-level screens, -along with the setup flow. Define these +along with the setup flow. Define these route names as constants so that they can be referenced within code. @@ -61,17 +61,17 @@ const routeDeviceSetupFinishedPage = 'finished'; The home and settings screens are referenced with static names. The setup flow pages, however, -use two paths to create their route names: +use two paths to create their route names: a `/setup/` prefix followed by the name of the specific page. By combining the two paths, your `Navigator` can determine that a route name is intended for the setup flow without -recognizing all the individual pages associated with +recognizing all the individual pages associated with the setup flow. The top-level `Navigator` isn't responsible for identifying individual setup flow pages. Therefore, your top-level `Navigator` needs to parse the incoming route name to -identify the setup flow prefix. Needing to parse the route name +identify the setup flow prefix. Needing to parse the route name means that you can't use the `routes` property of your top-level `Navigator`. Instead, you must provide a function for the `onGenerateRoute` property. @@ -105,11 +105,11 @@ onGenerateRoute: (settings) { }, ``` -Notice that the home and settings routes are matched with exact +Notice that the home and settings routes are matched with exact route names. However, the setup flow route condition only checks for a prefix. If the route name contains the setup flow prefix, then the rest of the route name is ignored -and passed on to the `SetupFlow` widget to process. +and passed on to the `SetupFlow` widget to process. This splitting of the route name is what allows the top-level `Navigator` to be agnostic toward the various subroutes within the setup flow. @@ -139,7 +139,7 @@ The setup flow displays a persistent app bar that appears across all pages. Return a `Scaffold` widget from your `SetupFlow` -widget's `build()` method, +widget's `build()` method, and include the desired `AppBar` widget. @@ -156,7 +156,7 @@ PreferredSizeWidget _buildFlowAppBar() { The app bar displays a back arrow and exits the setup flow when the back arrow is pressed. However, -exiting the flow causes the user to lose all progress. +exiting the flow causes the user to lose all progress. Therefore, the user is prompted to confirm whether they want to exit the setup flow. @@ -237,14 +237,14 @@ When the user taps the back arrow in the app bar, or presses the back button on their device, an alert dialog pops up to confirm that the user wants to leave the setup flow. -If the user presses **Leave**, then the setup flow pops itself +If the user presses **Leave**, then the setup flow pops itself from the top-level navigation stack. If the user presses **Stay**, then the action is ignored. You might notice that the `Navigator.pop()` -is invoked by both the **Leave** and +is invoked by both the **Leave** and **Stay** buttons. To be clear, -this `pop()` action pops the alert dialog off +this `pop()` action pops the alert dialog off the navigation stack, not the setup flow. ## Generate nested routes @@ -328,37 +328,37 @@ one of four flow pages is returned. The first page, called `find_devices`, waits a few seconds to simulate network scanning. -After the wait period, the page invokes its callback. +After the wait period, the page invokes its callback. In this case, that callback is `_onDiscoveryComplete`. The setup flow recognizes that, when device discovery is complete, the device selection page should be shown. -Therefore, in `_onDiscoveryComplete`, the `_navigatorKey` +Therefore, in `_onDiscoveryComplete`, the `_navigatorKey` instructs the nested `Navigator` to navigate to the `select_device` page. The `select_device` page asks the user to select a device from a list of available devices. In this recipe, -only one device is presented to the user. +only one device is presented to the user. When the user taps a device, the `onDeviceSelected` callback is invoked. The setup flow recognizes that, -when a device is selected, the connecting page +when a device is selected, the connecting page should be shown. Therefore, in `_onDeviceSelected`, the `_navigatorKey` instructs the nested `Navigator` to navigate to the `"connecting"` page. The `connecting` page works the same way as the `find_devices` page. The `connecting` page waits -for a few seconds and then invokes its callback. +for a few seconds and then invokes its callback. In this case, the callback is `_onConnectionEstablished`. The setup flow recognizes that, when a connection is established, the final page should be shown. Therefore, -in `_onConnectionEstablished`, the `_navigatorKey` +in `_onConnectionEstablished`, the `_navigatorKey` instructs the nested `Navigator` to navigate to the `finished` page. The `finished` page provides the user with a **Finish** button. When the user taps **Finish**, -the `_exitSetup` callback is invoked, which pops the entire +the `_exitSetup` callback is invoked, which pops the entire setup flow off the top-level `Navigator` stack, taking the user back to the home screen. diff --git a/src/content/cookbook/effects/shimmer-loading.md b/src/content/cookbook/effects/shimmer-loading.md index 437aa4beb62..99b82ffcaac 100644 --- a/src/content/cookbook/effects/shimmer-loading.md +++ b/src/content/cookbook/effects/shimmer-loading.md @@ -7,10 +7,10 @@ description: How to implement a shimmer loading effect. Loading times are unavoidable in application development. From a user experience (UX) perspective, -the most important thing is to show your users +the most important thing is to show your users that loading is taking place. One popular approach to communicate to users that data is loading is to -display a chrome color with a shimmer animation over +display a chrome color with a shimmer animation over the shapes that approximate the type of content that is loading. The following animation shows the app's behavior: @@ -27,22 +27,22 @@ so that you can easily validate your implementation. The shapes that shimmer in this effect are independent from the actual content that eventually loads. -Therefore, the goal is to display shapes that represent -the eventual content as accurately as possible. +Therefore, the goal is to display shapes that represent +the eventual content as accurately as possible. Displaying accurate shapes is easy in situations where the content has a clear boundary. For example, in this recipe, there are some circular images and some rounded rectangle images. -You can draw shapes that precisely match the outlines +You can draw shapes that precisely match the outlines of those images. On the other hand, consider the text that appears beneath the rounded rectangle images. You won't know how many lines of -text exist until the text loads. +text exist until the text loads. Therefore, there is no point in trying to draw a rectangle for every line of text. Instead, while the data is loading, you draw a couple of very thin rounded rectangles that -represent the text that will appear. The shape and size +represent the text that will appear. The shape and size doesn't quite match, but that is OK. Start with the circular list items at the top of the screen. @@ -82,9 +82,9 @@ As long as your widgets display some kind of shape, you can apply the shimmer effect in this recipe. Similar to the `CircleListItem` widgets, -ensure that the `CardListItem` widgets +ensure that the `CardListItem` widgets display a color where the image will appear. -Also, in the `CardListItem` widget, +Also, in the `CardListItem` widget, switch between the display of the text and the rectangles based on the current loading status. @@ -177,14 +177,14 @@ with a single gradient that looks like a shimmer. ## Paint the shimmer gradient -The key to the effect achieved in this recipe is to use a widget +The key to the effect achieved in this recipe is to use a widget called [`ShaderMask`][]. The `ShaderMask` widget, as the name suggests, applies a shader to its child, but only in the areas where the child already painted something. For example, -you'll apply a shader to only the black shapes that you +you'll apply a shader to only the black shapes that you configured earlier. -Define a chrome-colored, linear gradient that gets applied to the +Define a chrome-colored, linear gradient that gets applied to the shimmer shapes. @@ -261,33 +261,33 @@ Widget _buildListItem() { ``` When your shapes are loading, they now display -the shimmer gradient that is +the shimmer gradient that is returned from the `shaderCallback`. This is a big step in the right direction, but there's a problem with this gradient display. -Each `CircleListItem` widget and each `CardListItem` widget +Each `CircleListItem` widget and each `CardListItem` widget displays a new version of the gradient. -For this recipe, the entire screen should +For this recipe, the entire screen should look like one, big shimmering surface. You solve this problem in the next step. ## Paint one big shimmer To paint one big shimmer across the screen, -each `ShimmerLoading` widget needs +each `ShimmerLoading` widget needs to paint the same full-screen gradient based on the position of that `ShimmerLoading` -widget on the screen. +widget on the screen. To be more precise, rather than assume that the shimmer should take up the entire screen, there should be some area that shares the shimmer. Maybe that area takes up the entire screen, -or maybe it doesn't. The way to solve this +or maybe it doesn't. The way to solve this kind of problem in Flutter is to define another widget that sits above all of the `ShimmerLoading` widgets -in the widget tree, and call it `Shimmer`. +in the widget tree, and call it `Shimmer`. Then, each `ShimmerLoading` widget gets a reference to the `Shimmer` ancestor and requests the desired size and gradient to display. @@ -429,10 +429,10 @@ give the appearance of a shimmering shine. The `LinearGradient` has a property called `transform` that can be used to transform the appearance of the gradient, -for example, to move it horizontally. +for example, to move it horizontally. The `transform` property accepts a `GradientTransform` instance. -Define a class called `_SlidingGradientTransform` that implements +Define a class called `_SlidingGradientTransform` that implements `GradientTransform` to achieve the appearance of horizontal sliding. @@ -493,7 +493,7 @@ LinearGradient get gradient => LinearGradient( The gradient now animates, but your individual `ShimmerLoading` widgets don't repaint themselves -as the gradient changes. Therefore, it looks like nothing +as the gradient changes. Therefore, it looks like nothing is happening. Expose the `_shimmerController` from `ShimmerState` @@ -543,7 +543,7 @@ class _ShimmerLoadingState extends State { Congratulations! You now have a full-screen, -animated shimmer effect that turns +animated shimmer effect that turns on and off as the content loads. ## Interactive example diff --git a/src/content/cookbook/effects/staggered-menu-animation.md b/src/content/cookbook/effects/staggered-menu-animation.md index 53811dd236d..7462a90f269 100644 --- a/src/content/cookbook/effects/staggered-menu-animation.md +++ b/src/content/cookbook/effects/staggered-menu-animation.md @@ -8,10 +8,10 @@ description: How to implement a staggered menu animation. A single app screen might contain multiple animations. Playing all of the animations at the same time can be overwhelming. Playing the animations one after the other -can take too long. A better option is to stagger the animations. +can take too long. A better option is to stagger the animations. Each animation begins at a different time, but the animations overlap to create a shorter duration. -In this recipe, you build a drawer menu with animated +In this recipe, you build a drawer menu with animated content that is staggered and has a button that pops in at the bottom. @@ -22,11 +22,11 @@ The following animation shows the app's behavior: ## Create the menu without animations The drawer menu displays a list of titles, -followed by a Get started button at +followed by a Get started button at the bottom of the menu. Define a stateful widget called `Menu` -that displays the list and button +that displays the list and button in static locations. @@ -146,7 +146,7 @@ class _MenuState extends State with SingleTickerProviderStateMixin { The length of the delay before every animation is up to you. Define the animation delays, -individual animation durations, and the total +individual animation durations, and the total animation duration. @@ -167,7 +167,7 @@ class _MenuState extends State with SingleTickerProviderStateMixin { In this case, all the animations are delayed by 50 ms. After that, list items begin to appear. -Each list item's appearance is delayed by 50 ms after the +Each list item's appearance is delayed by 50 ms after the previous list item begins to slide in. Each list item takes 250 ms to slide from right to left. After the last list item begins to slide in, @@ -184,15 +184,15 @@ The desired animation times are shown in the following diagram: To animate a value during a subsection of a larger animation, Flutter provides the `Interval` class. -An `Interval` takes a start time percentage and an end +An `Interval` takes a start time percentage and an end time percentage. That `Interval` can then be used to animate a value between those start and end times, -instead of using the entire animation's start and -end times. For example, given an animation that takes 1 second, +instead of using the entire animation's start and +end times. For example, given an animation that takes 1 second, an interval from 0.2 to 0.5 would start at 200 ms -(20%) and end at 500 ms (50%). +(20%) and end at 500 ms (50%). -Declare and calculate each list item's `Interval` and the +Declare and calculate each list item's `Interval` and the bottom button `Interval`. @@ -346,7 +346,7 @@ Widget _buildGetStartedButton() { ``` Congratulations! -You have an animated menu where the appearance of each +You have an animated menu where the appearance of each list item is staggered, followed by a bottom button that pops into place. diff --git a/src/content/cookbook/forms/text-field-changes.md b/src/content/cookbook/forms/text-field-changes.md index 7a0abdf2fbf..b50e85574c5 100644 --- a/src/content/cookbook/forms/text-field-changes.md +++ b/src/content/cookbook/forms/text-field-changes.md @@ -22,7 +22,7 @@ The simplest approach is to supply an [`onChanged()`][] callback to a [`TextField`][] or a [`TextFormField`][]. Whenever the text changes, the callback is invoked. -In this example, print the current value and length of the text field +In this example, print the current value and length of the text field to the console every time the text changes. It's important to use [characters][] when dealing with user input, diff --git a/src/content/cookbook/games/achievements-leaderboard.md b/src/content/cookbook/games/achievements-leaderboard.md index fea49d578be..fd49c730649 100644 --- a/src/content/cookbook/games/achievements-leaderboard.md +++ b/src/content/cookbook/games/achievements-leaderboard.md @@ -7,7 +7,7 @@ description: > Gamers have various motivations for playing games. -In broad strokes, there are four major motivations: +In broad strokes, there are four major motivations: [immersion, achievement, cooperation, and competition][]. No matter the game you build, some players want to *achieve* in it. This could be trophies won or secrets unlocked. @@ -22,7 +22,7 @@ centralized services for achievements and leaderboards. Players can view achievements from all their games in one place and developers don't need to re-implement them for every game. -This recipe demonstrates how to use the [`games_services` package][] +This recipe demonstrates how to use the [`games_services` package][] to add achievements and leaderboard functionality to your mobile game. [`games_services` package]: {{site.pub-pkg}}/games_services @@ -67,8 +67,8 @@ To enable Game Center (GameKit) on iOS: To enable *Play Games Services* on Android: 1. If you haven't already, go to [Google Play Console][] - and register your game there. - + and register your game there. + ![Screenshot of the 'Create app' button in Google Play Console](/assets/images/docs/cookbook/google-play-create-app.png) 2. Still in Google Play Console, select *Play Games Services* → *Setup @@ -79,8 +79,8 @@ To enable *Play Games Services* on Android: Among other things, you'll need to set up an OAuth consent screen in Google Cloud Console. If at any point you feel lost, consult the - official [Play Games Services guide][]. - + official [Play Games Services guide][]. + ![Screenshot showing the Games Services section in Google Play Console](/assets/images/docs/cookbook/play-console-play-games-services.png) 3. When done, you can start adding leaderboards and achievements in @@ -191,7 +191,7 @@ the raw functionality of the `games_services` plugin. 3. To display the achievements in your own UI, use [`GamesServices.loadAchievements()`][]. - + [`GamesServices.loadAchievements()`]: {{site.pub-api}}/games_services/latest/games_services/GamesServices/loadAchievements.html ## 4. Submit scores @@ -234,7 +234,7 @@ leaderboards. 3. If you want to display the leaderboard scores in your own UI, you can fetch them with [`GamesServices.loadLeaderboardScores()`][]. - + [`GamesServices.loadLeaderboardScores()`]: {{site.pub-api}}/games_services/latest/games_services/GamesServices/loadLeaderboardScores.html ## 5. Next steps @@ -250,7 +250,7 @@ all 10 pieces of the McGuffin." Each game has different needs from game services. -To start, you might want to create this controller +To start, you might want to create this controller in order to keep all achievements & leaderboards logic in one place: diff --git a/src/content/cookbook/games/firestore-multiplayer.md b/src/content/cookbook/games/firestore-multiplayer.md index db511bd9cf0..2f712225c3c 100644 --- a/src/content/cookbook/games/firestore-multiplayer.md +++ b/src/content/cookbook/games/firestore-multiplayer.md @@ -136,7 +136,7 @@ Dart code in that guide, return to this recipe. ```dart import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_core/firebase_core.dart'; - + import 'firebase_options.dart'; ``` @@ -146,7 +146,7 @@ Dart code in that guide, return to this recipe. ```dart WidgetsFlutterBinding.ensureInitialized(); - + await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); ``` diff --git a/src/content/cookbook/lists/basic-list.md b/src/content/cookbook/lists/basic-list.md index 5c2877cbbbf..5d10694e94d 100644 --- a/src/content/cookbook/lists/basic-list.md +++ b/src/content/cookbook/lists/basic-list.md @@ -60,7 +60,7 @@ class MyApp extends StatelessWidget { ``` diff --git a/src/content/cookbook/lists/spaced-items.md b/src/content/cookbook/lists/spaced-items.md index a7f27c06095..596a7fdd215 100644 --- a/src/content/cookbook/lists/spaced-items.md +++ b/src/content/cookbook/lists/spaced-items.md @@ -1,6 +1,6 @@ --- title: List with spaced items -description: How to create a list with spaced or expanded items +description: How to create a list with spaced or expanded items --- @@ -67,7 +67,7 @@ The [`ConstrainedBox`][] widget imposes additional constraints to its child. Configure the constraint by setting the `minHeight` parameter to be the `maxHeight` of the [`LayoutBuilder`][] constraints. -This ensures that the child widget +This ensures that the child widget is constrained to have a minimum height equal to the available space provided by the [`LayoutBuilder`][] constraints, namely the maximum height of the [`BoxConstraints`][]. @@ -95,7 +95,7 @@ in case the items don't fit the screen. Finally, add a [`Column`][] as the child of the [`ConstrainedBox`][]. -To space the items evenly, +To space the items evenly, set the `mainAxisAlignment` to `MainAxisAlignment.spaceBetween`. @@ -119,7 +119,7 @@ LayoutBuilder( ); ``` -Alternatively, you can use the [`Spacer`][] widget +Alternatively, you can use the [`Spacer`][] widget to tune the spacing between the items, or the [`Expanded`][] widget, if you want one widget to take more space than others. diff --git a/src/content/cookbook/maintenance/error-reporting.md b/src/content/cookbook/maintenance/error-reporting.md index 1cca1c79c9d..6e4d3f6fec6 100644 --- a/src/content/cookbook/maintenance/error-reporting.md +++ b/src/content/cookbook/maintenance/error-reporting.md @@ -84,7 +84,7 @@ Alternatively, you can pass the DSN to Flutter using the `dart-define` tag: ### What does that give me? This is all you need for Sentry to -capture unhandled errors in Dart and native layers. +capture unhandled errors in Dart and native layers. This includes Swift, Objective-C, C, and C++ on iOS, and Java, Kotlin, C, and C++ on Android. diff --git a/src/content/cookbook/navigation/passing-data.md b/src/content/cookbook/navigation/passing-data.md index 50322e1eaa8..34c201abc3a 100644 --- a/src/content/cookbook/navigation/passing-data.md +++ b/src/content/cookbook/navigation/passing-data.md @@ -105,7 +105,7 @@ class TodosScreen extends StatelessWidget { } ``` -With Flutter's default styling, you're good to go without sweating about +With Flutter's default styling, you're good to go without sweating about things that you'd like to do later on! ## 4. Create a detail screen to display information about a todo diff --git a/src/content/cookbook/navigation/set-up-app-links.md b/src/content/cookbook/navigation/set-up-app-links.md index d1766760e8d..964c70617e1 100644 --- a/src/content/cookbook/navigation/set-up-app-links.md +++ b/src/content/cookbook/navigation/set-up-app-links.md @@ -48,9 +48,9 @@ It provides a simple API to handle complex routing scenarios. ```dart title="main.dart" import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; - + void main() => runApp(MaterialApp.router(routerConfig: router)); - + /// This handles '/' and '/details'. final router = GoRouter( routes: [ @@ -74,7 +74,7 @@ It provides a simple API to handle complex routing scenarios. ## 2. Modify AndroidManifest.xml - 1. Open the Flutter project with VS Code or Android Studio. + 1. Open the Flutter project with VS Code or Android Studio. 2. Navigate to `android/app/src/main/AndroidManifest.xml` file. 3. Add the following metadata tag and intent filter inside the `` tag with `.MainActivity`. @@ -179,8 +179,8 @@ The hosted file should look similar to this: 4. Verify that your browser can access this file. :::note -If you have multiple flavors, you can have many sha256_cert_fingerprint -values in the sha256_cert_fingerprints field. +If you have multiple flavors, you can have many sha256_cert_fingerprint +values in the sha256_cert_fingerprints field. Just add it to the sha256_cert_fingerprints list ::: diff --git a/src/content/cookbook/persistence/sqlite.md b/src/content/cookbook/persistence/sqlite.md index 4bf974b5f5f..70c6e62417f 100644 --- a/src/content/cookbook/persistence/sqlite.md +++ b/src/content/cookbook/persistence/sqlite.md @@ -96,7 +96,7 @@ to the database. This involves two steps: :::note In order to use the keyword `await`, the code must be placed inside an `async` function. You should place all the following -table functions inside `void main() async {}`. +table functions inside `void main() async {}`. ::: diff --git a/src/content/cookbook/plugins/google-mobile-ads.md b/src/content/cookbook/plugins/google-mobile-ads.md index 38441c5db47..a2311945a61 100644 --- a/src/content/cookbook/plugins/google-mobile-ads.md +++ b/src/content/cookbook/plugins/google-mobile-ads.md @@ -9,24 +9,24 @@ description: How to use the google_mobile_ads package to show ads in Flutter. {% comment %} This partly duplicates the AdMob documentation here: https://developers.google.com/admob/flutter/quick-start - + The added value of this page is that it's more straightforward for someone who just has a Flutter app or game and wants to add monetization to it. - + In short, this is a friendlier --- though not as comprehensive --- introduction to ads in Flutter. {% endcomment %} Many developers use advertising to monetize their mobile apps and games. -This allows their app to be downloaded free of charge, +This allows their app to be downloaded free of charge, which improves the app's popularity. ![An illustration of a smartphone showing an ad](/assets/images/docs/cookbook/ads-device.jpg){:.site-illustration} To add ads to your Flutter project, use -[AdMob](https://admob.google.com/home/), +[AdMob](https://admob.google.com/home/), Google's mobile advertising platform. This recipe demonstrates how to use the [`google_mobile_ads`]({{site.pub-pkg}}/google_mobile_ads) @@ -63,7 +63,7 @@ cookbook recipe. To use Ad Manager, follow the Update your Android and iOS configurations to include your App IDs. {% comment %} - Content below is more or less a copypaste from devsite: + Content below is more or less a copypaste from devsite: https://developers.google.com/admob/flutter/quick-start#platform_specific_setup {% endcomment %} @@ -79,14 +79,14 @@ Add your AdMob app ID to your Android app. `com.google.android.gms.ads.APPLICATION_ID`. 4. Set the `android:value` element with the value to your own AdMob app - ID that you got in the previous step. + ID that you got in the previous step. Include them in quotes as shown: ```xml ... - + main() { } ``` -The `integrationDriver` function has a `responseDataCallback` -which you can customize. +The `integrationDriver` function has a `responseDataCallback` +which you can customize. By default, it writes the results to the `integration_response_data.json` file, but you can customize it to generate a summary like in this example. @@ -142,9 +142,9 @@ flutter drive \ --profile ``` -The `--profile` option means to compile the app for the "profile mode" -rather than the "debug mode", so that the benchmark result is closer to -what will be experienced by end users. +The `--profile` option means to compile the app for the "profile mode" +rather than the "debug mode", so that the benchmark result is closer to +what will be experienced by end users. :::note Run the command with `--no-dds` when running on a mobile device or emulator. diff --git a/src/content/cookbook/testing/unit/introduction.md b/src/content/cookbook/testing/unit/introduction.md index 349c97852a9..a980dd453f2 100644 --- a/src/content/cookbook/testing/unit/introduction.md +++ b/src/content/cookbook/testing/unit/introduction.md @@ -30,7 +30,7 @@ see the [test package documentation][]. ## 1. Add the test dependency -The `test` package provides the core functionality for +The `test` package provides the core functionality for writing tests in Dart. This is the best approach when writing packages consumed by web, server, and Flutter apps. diff --git a/src/content/cookbook/testing/unit/mocking.md b/src/content/cookbook/testing/unit/mocking.md index 9f9a2fdb7d5..f632ad887d5 100644 --- a/src/content/cookbook/testing/unit/mocking.md +++ b/src/content/cookbook/testing/unit/mocking.md @@ -90,7 +90,7 @@ Future fetchAlbum(http.Client client) async { } ``` -In your app code, you can provide an `http.Client` to the `fetchAlbum` method +In your app code, you can provide an `http.Client` to the `fetchAlbum` method directly with `fetchAlbum(http.Client())`. `http.Client()` creates a default `http.Client`. diff --git a/src/content/cookbook/testing/widget/orientation.md b/src/content/cookbook/testing/widget/orientation.md index 75a7cd9ffa0..ecd1ec11a0d 100644 --- a/src/content/cookbook/testing/widget/orientation.md +++ b/src/content/cookbook/testing/widget/orientation.md @@ -51,7 +51,7 @@ group your future orientation tests: import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:orientation_tests/main.dart'; - + void main() { group('Orientation', () { // ··· @@ -74,7 +74,7 @@ only `2` columns of data appear in the app: testWidgets('Displays 2 columns in portrait mode', (tester) async { // Build the app. await tester.pumpWidget(const MyApp()); - + // Change to portrait. tester.view.physicalSize = const Size(600, 800); tester.view.devicePixelRatio = 1.0; @@ -82,13 +82,13 @@ only `2` columns of data appear in the app: tester.view.resetPhysicalSize(); }); await tester.pump(); - + // Verify initial orientation is portrait. final orientation = MediaQuery.of( tester.element(find.byType(OrientationList)), ).orientation; expect(orientation, Orientation.portrait); - + // Verify there are only 2 columns in portrait mode. final gridViewFinder = find.byType(GridView); final gridView = tester.widget(gridViewFinder); @@ -113,7 +113,7 @@ only `3` columns of data appear in the app: testWidgets('Displays 3 columns in landscape mode', (tester) async { // Build the app. await tester.pumpWidget(const MyApp()); - + // Change to landscape. tester.view.physicalSize = const Size(800, 600); tester.view.devicePixelRatio = 1.0; @@ -121,13 +121,13 @@ only `3` columns of data appear in the app: tester.view.resetPhysicalSize(); }); await tester.pump(); - + // Verify initial orientation is landscape. final orientation = MediaQuery.of( tester.element(find.byType(OrientationList)), ).orientation; expect(orientation, Orientation.landscape); - + // Verify there are only 3 columns in landscape mode. final gridViewFinder = find.byType(GridView); final gridView = tester.widget(gridViewFinder); diff --git a/src/content/cookbook/testing/widget/scrolling.md b/src/content/cookbook/testing/widget/scrolling.md index 55c5b339d18..7ee228cd5c0 100644 --- a/src/content/cookbook/testing/widget/scrolling.md +++ b/src/content/cookbook/testing/widget/scrolling.md @@ -17,7 +17,7 @@ which is included in the [`flutter_test`][] package: In this recipe, learn how to scroll through a list of items to verify a specific widget is being displayed, -and the pros and cons of different approaches. +and the pros and cons of different approaches. This recipe uses the following steps: diff --git a/src/content/cookbook/testing/widget/tap-drag.md b/src/content/cookbook/testing/widget/tap-drag.md index e005d3b6996..adeb37f3e2e 100644 --- a/src/content/cookbook/testing/widget/tap-drag.md +++ b/src/content/cookbook/testing/widget/tap-drag.md @@ -294,4 +294,3 @@ class _TodoListState extends State { [`tap()`]: {{site.api}}/flutter/flutter_test/WidgetController/tap.html [`TextField`]: {{site.api}}/flutter/material/TextField-class.html [`WidgetTester`]: {{site.api}}/flutter/flutter_test/WidgetTester-class.html - diff --git a/src/content/dash/index.md b/src/content/dash/index.md index a80d085d778..0a2aae51eab 100644 --- a/src/content/dash/index.md +++ b/src/content/dash/index.md @@ -126,20 +126,20 @@ Dash 2.0 and 2.1 **Please, don't depict Dash with a curved beak.** * We also have Mega-Dash, a life-sized mascot who is currently resting in a Google office. - + ![Mega-Dash in the office](/assets/images/dash/MegaDashChilling.png){:width="50%"} - + Mega-Dash made her first appearance at the [Flutter Interact][] event in Brooklyn, New York, on December 11, 2019. - + * We also have a Dash puppet that Shams made from one of the first plushies. - + ![Nilay and the Dash puppet](/assets/images/dash/NilayDashPuppet.png){:width="50%"} - + A number of our YouTube videos feature the Dash puppet, voiced by Emily Fortuna, one of the early (and much loved) Flutter Developer Advocates. diff --git a/src/content/data-and-backend/google-apis.md b/src/content/data-and-backend/google-apis.md index c6d8696e55c..6dfb1649304 100644 --- a/src/content/data-and-backend/google-apis.md +++ b/src/content/data-and-backend/google-apis.md @@ -30,7 +30,7 @@ To add authentication to Firebase explicitly, check out the [Add a user authentication flow to a Flutter app using FirebaseUI][fb-lab] codelab and the [Get Started with Firebase Authentication on Flutter][fb-auth] docs. - + [fb-lab]: {{site.firebase}}/codelabs/firebase-auth-in-flutter-apps [Calendar]: {{site.pub-api}}/googleapis/latest/calendar_v3/calendar_v3-library.html [fb-auth]: {{site.firebase}}/docs/auth/flutter/start diff --git a/src/content/data-and-backend/networking.md b/src/content/data-and-backend/networking.md index 19707c79748..5b049d8bcb5 100644 --- a/src/content/data-and-backend/networking.md +++ b/src/content/data-and-backend/networking.md @@ -27,7 +27,7 @@ manifest (`AndroidManifest.xml`): ### macOS -macOS apps must allow network access in the relevant `*.entitlements` files. +macOS apps must allow network access in the relevant `*.entitlements` files. ```xml com.apple.security.network.client @@ -41,7 +41,7 @@ Learn more about [setting up entitlements][]. ## Samples For a practical sample of various networking tasks (incl. fetching data, -WebSockets, and parsing data in the background) see the +WebSockets, and parsing data in the background) see the [networking cookbook recipes](/cookbook/networking). [declare]: {{site.android-dev}}/training/basics/network-ops/connecting diff --git a/src/content/data-and-backend/serialization/json.md b/src/content/data-and-backend/serialization/json.md index ef4a4f23ee2..c98cf000f57 100644 --- a/src/content/data-and-backend/serialization/json.md +++ b/src/content/data-and-backend/serialization/json.md @@ -219,7 +219,7 @@ decoded. ::: However, real-world scenarios are not always that simple. -Sometimes JSON API responses are more complex, for example since they +Sometimes JSON API responses are more complex, for example since they contain nested JSON objects that must be parsed through their own model class. @@ -239,7 +239,7 @@ on pub.dev that generate JSON serialization code, [`json_serializable`][] and [`built_value`][]. How do you choose between these packages? The `json_serializable` package allows you to make regular -classes serializable by using annotations, +classes serializable by using annotations, whereas the `built_value` package provides a higher-level way of defining immutable value classes that can also be serialized to JSON. @@ -320,16 +320,16 @@ you can use the `@JsonKey` annotation with a name parameter: final int registrationDateMillis; ``` -It's best if both server and client follow the same naming strategy. -`@JsonSerializable()` provides `fieldRename` enum for totally converting dart +It's best if both server and client follow the same naming strategy. +`@JsonSerializable()` provides `fieldRename` enum for totally converting dart fields into JSON keys. Modifying `@JsonSerializable(fieldRename: FieldRename.snake)` is equivalent to adding `@JsonKey(name: '')` to each field. Sometimes server data is uncertain, so it is necessary to verify and protect data - on client. -Other commonly used `@JsonKey` annotations include: + on client. +Other commonly used `@JsonKey` annotations include: ```dart /// Tell json_serializable to use "defaultValue" if the JSON doesn't @@ -337,13 +337,13 @@ Other commonly used `@JsonKey` annotations include: @JsonKey(defaultValue: false) final bool isAdult; -/// When `true` tell json_serializable that JSON must contain the key, +/// When `true` tell json_serializable that JSON must contain the key, /// If the key doesn't exist, an exception is thrown. @JsonKey(required: true) final String id; -/// When `true` tell json_serializable that generated code should -/// ignore this field completely. +/// When `true` tell json_serializable that generated code should +/// ignore this field completely. @JsonKey(ignore: true) final String verificationCode; ``` @@ -457,7 +457,7 @@ class User { } ``` -Running +Running `dart run build_runner build --delete-conflicting-outputs` in the terminal creates the `*.g.dart` file, but the private `_$UserToJson()` function diff --git a/src/content/data-and-backend/state-mgmt/simple.md b/src/content/data-and-backend/state-mgmt/simple.md index 98f97fbd923..0eab82985ff 100644 --- a/src/content/data-and-backend/state-mgmt/simple.md +++ b/src/content/data-and-backend/state-mgmt/simple.md @@ -26,7 +26,7 @@ That said, if you have a strong background in state management from other reactive frameworks, you can find packages and tutorials listed on the [options page][]. -## Our example +## Our example An animated gif showing a Flutter app in use. It starts with the user on a login screen. They log in and are taken to the catalog screen, with a list of items. The click on several items, and as they do so, the items are marked as "added". The user clicks on a button and gets taken to the cart view. They see the items there. They go back to the catalog, and the items they bought still show "added". End of animation. @@ -451,10 +451,10 @@ If you want something simpler, see what the simple Counter app looks like when [built with `provider`][]. -By following along with these articles, you've greatly -improved your ability to create state-based applications. -Try building an application with `provider` yourself to -master these skills. +By following along with these articles, you've greatly +improved your ability to create state-based applications. +Try building an application with `provider` yourself to +master these skills. [built with `provider`]: {{site.repo.samples}}/tree/main/provider_counter [check out the example]: {{site.repo.samples}}/tree/main/provider_shopper diff --git a/src/content/deployment/android.md b/src/content/deployment/android.md index 257d81e439f..89d6be69a71 100644 --- a/src/content/deployment/android.md +++ b/src/content/deployment/android.md @@ -26,9 +26,9 @@ This guide explains how to perform the following tasks: * [Android release FAQ](#android-release-faq) :::note -Throughout this page, `[project]` refers to +Throughout this page, `[project]` refers to the directory that your application is in. While following -these instructions, substitute `[project]` with +these instructions, substitute `[project]` with your app's directory. ::: diff --git a/src/content/deployment/cd.md b/src/content/deployment/cd.md index a5d832fa625..94b2656c1a6 100644 --- a/src/content/deployment/cd.md +++ b/src/content/deployment/cd.md @@ -31,13 +31,13 @@ You can use fastlane with the following tooling: * [CircleCI][] * [Building and deploying Flutter apps with Fastlane][] -This guide shows how to set up fastlane and then integrate it with -your existing testing and continuous integration (CI) workflows. +This guide shows how to set up fastlane and then integrate it with +your existing testing and continuous integration (CI) workflows. For more information, see "Integrating fastlane with existing workflow". ## fastlane -[fastlane][] is an open-source tool suite to automate releases and deployments +[fastlane][] is an open-source tool suite to automate releases and deployments for your app. ### Local setup @@ -93,7 +93,7 @@ Visit the [fastlane docs][fastlane] for more info. * ![iOS](/assets/images/docs/cd/ios.png) On iOS, follow the [fastlane iOS beta deployment guide][]. You can specify the archive path to avoid rebuilding the project. For example: - + ```ruby build_app( skip_build_archive: true, @@ -125,8 +125,8 @@ The main thing to consider is that since cloud instances are ephemeral and untrusted, you won't be leaving your credentials like your Play Store service account JSON or your iTunes distribution certificate on the server. -Continuous Integration (CI) systems generally support encrypted environment -variables to store private data. You can pass these environment variables +Continuous Integration (CI) systems generally support encrypted environment +variables to store private data. You can pass these environment variables using `--dart-define MY_VAR=MY_VALUE` while building the app. **Take precaution not to re-echo those variable values back onto the console in @@ -138,7 +138,7 @@ secrets in pull requests that you accept and merge. 1. Make login credentials ephemeral. * ![Android](/assets/images/docs/cd/android.png) On Android: * Remove the `json_key_file` field from `Appfile` and store the string - content of the JSON in your CI system's encrypted variable. + content of the JSON in your CI system's encrypted variable. Read the environment variable directly in your `Fastfile`. ```plaintext upload_to_play_store( @@ -205,7 +205,7 @@ testing, and distributing apps and frameworks for Apple platforms. ### Custom build script -Xcode Cloud recognizes [custom build scripts][] that can be +Xcode Cloud recognizes [custom build scripts][] that can be used to perform additional tasks at a designated time. It also includes a set of [predefined environment variables][], such as `$CI_WORKSPACE`, which is the location of your cloned repository. diff --git a/src/content/deployment/flavors-ios.md b/src/content/deployment/flavors-ios.md index 178df634e69..d2c3de29a62 100644 --- a/src/content/deployment/flavors-ios.md +++ b/src/content/deployment/flavors-ios.md @@ -82,7 +82,7 @@ always start with an existing project. * Open the **project navigator** (**View** > **Navigators** > **Project**). - + * In the **project navigator**, at the top, select **Runner**. @@ -113,7 +113,7 @@ always start with an existing project. (**Product > Scheme > Manage Schemes**) and make sure that the **Shared** checkbox to the right of your new scheme is checked. - ::: + ::: 1. Create configurations for the schemes in Xcode: @@ -137,7 +137,7 @@ always start with an existing project. * Repeat the previous step for the `Release` configurations and the `Profile` configurations. - + * When finished, check to make sure that you have the following configurations: @@ -189,7 +189,7 @@ always start with an existing project. * Select the `staging` scheme (**Product > Schemes > staging**). - + * To the right of `staging` in the toolbar, select the iOS device you want to test against. In the following example, the device is `iPhone 16 Pro`. @@ -402,7 +402,7 @@ A bundle identifier is a unique identifier for your application on Apple's platforms. If you are using multiple Xcode schemes as Flutter flavors, you can have Apple treat each scheme as a separate application. To do this, you need -to assign a different bundle identifier to each scheme. +to assign a different bundle identifier to each scheme. This allows you to test new features or bug fixes in one version of the app (for example `staging`) without affecting another version of the app (for example, `production`). @@ -433,7 +433,7 @@ and `production` in an iOS project called `flavors_example`. * Debug, Profile, Release, Debug-production, Profile-production, Release-production: - + `com.example.flavorsExample` 1. Ensure that these bundle identifiers are included in @@ -494,7 +494,7 @@ distribution. One way that you can use build settings with Flutter flavors is to assign those build settings to Xcode build configurations. For example, you might want to assign different API URLs to `Debug-staging` and -`Debug-production`. For example: +`Debug-production`. For example: ```plaintext title="debug-staging-settings.xcconfig" # Debug-staging build settings diff --git a/src/content/deployment/flavors.md b/src/content/deployment/flavors.md index 7e611660cd5..1c28dd688f9 100644 --- a/src/content/deployment/flavors.md +++ b/src/content/deployment/flavors.md @@ -258,7 +258,7 @@ flavor your deployed app is using. The following steps show how to add a distinct icon for two product flavors called `staging` and `production` in a -project called `flavors_example`. +project called `flavors_example`. 1. Prepare your icons: @@ -360,4 +360,4 @@ the following resources: * [How to Setup Flutter & Firebase with Multiple Flavors using the FlutterFire CLI][flutterfireCLI] [Build flavors in Flutter (Android and iOS) with Firebase]: {{site.medium}}/@animeshjain/build-flavors-in-flutter-android-and-ios-with-different-firebase-projects-per-flavor-27c5c5dac10b -[flutterfireCLI]: https://codewithandrea.com/articles/flutter-firebase-multiple-flavors-flutterfire-cli/ \ No newline at end of file +[flutterfireCLI]: https://codewithandrea.com/articles/flutter-firebase-multiple-flavors-flutterfire-cli/ diff --git a/src/content/deployment/linux.md b/src/content/deployment/linux.md index 852294b5ec4..9f7c244d4d8 100644 --- a/src/content/deployment/linux.md +++ b/src/content/deployment/linux.md @@ -114,7 +114,7 @@ slots: interface: dbus bus: session name: org.bar.super_cool_app # adjust accordingly to your app name and - + apps: super-cool-app: command: super_cool_app @@ -176,7 +176,7 @@ grade: stable This section defines the application(s) that exist inside the snap. There can be one or more applications per snap. This example -has a single application—super_cool_app. +has a single application—super_cool_app. ```yaml apps: @@ -206,34 +206,34 @@ apps: access to the network. **DBus interface** -: The [DBus interface][] provides a way for snaps to - communicate over DBus. The snap providing the DBus - service declares a slot with the well-known DBus name - and which bus it uses. Snaps wanting to communicate - with the providing snap's service declare a plug for - the providing snap. Note that a snap declaration is - needed for your snap to be delivered via the snap store - and claim this well-known DBus name (simply upload the - snap to the store and request a manual review and +: The [DBus interface][] provides a way for snaps to + communicate over DBus. The snap providing the DBus + service declares a slot with the well-known DBus name + and which bus it uses. Snaps wanting to communicate + with the providing snap's service declare a plug for + the providing snap. Note that a snap declaration is + needed for your snap to be delivered via the snap store + and claim this well-known DBus name (simply upload the + snap to the store and request a manual review and a reviewer will take a look). - When a providing snap is installed, snapd will - generate security policy that will allow it to - listen on the well-known DBus name on the specified - bus. If the system bus is specified, snapd will also - generate DBus bus policy that allows 'root' to own - the name and any user to communicate with the - service. Non-snap processes are allowed to - communicate with the providing snap following - traditional permissions checks. Other (consuming) - snaps might only communicate with the providing + When a providing snap is installed, snapd will + generate security policy that will allow it to + listen on the well-known DBus name on the specified + bus. If the system bus is specified, snapd will also + generate DBus bus policy that allows 'root' to own + the name and any user to communicate with the + service. Non-snap processes are allowed to + communicate with the providing snap following + traditional permissions checks. Other (consuming) + snaps might only communicate with the providing snap by connecting the snaps' interface. - + ```plaintext dbus-super-cool-app: # adjust accordingly to your app name interface: dbus bus: session - name: dev.site.super_cool_app + name: dev.site.super_cool_app ``` ### Parts @@ -267,16 +267,16 @@ parts: ## Desktop file and icon -Desktop entry files are used to add an application -to the desktop menu. These files specify the name and +Desktop entry files are used to add an application +to the desktop menu. These files specify the name and icon of your application, the categories it belongs to, -related search keywords and more. These files have the -extension .desktop and follow the XDG Desktop Entry +related search keywords and more. These files have the +extension .desktop and follow the XDG Desktop Entry Specification version 1.1. - + ### Flutter super-cool-app.desktop example -Place the .desktop file in your Flutter project +Place the .desktop file in your Flutter project under `/snap/gui/super-cool-app.desktop`. **Notice**: icon and .desktop file name must be the same as your app name in @@ -288,14 +288,14 @@ For example: [Desktop Entry] Name=Super Cool App Comment=Super Cool App that does everything -Exec=super-cool-app +Exec=super-cool-app Icon=${SNAP}/meta/gui/super-cool-app.png # Replace name with your app name. Terminal=false Type=Application Categories=Education; # Adjust accordingly your snap category. ``` -Place your icon with .png extension in your Flutter +Place your icon with .png extension in your Flutter project under `/snap/gui/super-cool-app.png`. @@ -309,7 +309,7 @@ To use the Multipass VM backend: ```console $ snapcraft -``` +``` To use the LXD container backend: diff --git a/src/content/deployment/macos.md b/src/content/deployment/macos.md index 24b31ceb109..1009554c233 100644 --- a/src/content/deployment/macos.md +++ b/src/content/deployment/macos.md @@ -88,7 +88,7 @@ In the **Identity** section: `App Category` : The app category under which your app will be listed on the Mac App Store. This cannot be none. -`Bundle Identifier` +`Bundle Identifier` : The App ID you registered on App Store Connect. In the **Deployment info** section: @@ -123,7 +123,7 @@ For a detailed overview of app signing, see ## Configuring the app's name, bundle identifier and copyright -The configuration for the product identifiers are centralized +The configuration for the product identifiers are centralized in `macos/Runner/Configs/AppInfo.xcconfig`. For the app's name, set `PRODUCT_NAME`, for the copyright set `PRODUCT_COPYRIGHT`, and finally set `PRODUCT_BUNDLE_IDENTIFIER` for the app's @@ -218,7 +218,7 @@ For more details, see ## Create a build archive with Codemagic CLI tools This step covers creating a build archive and uploading -your build to App Store Connect using Flutter build commands +your build to App Store Connect using Flutter build commands and [Codemagic CLI Tools][codemagic_cli_tools] executed in a terminal in the Flutter project directory. @@ -282,7 +282,7 @@ app-store-connect fetch-signing-files YOUR.APP.BUNDLE_ID \ ``` Where `cert_key` is either your exported Mac App Distribution certificate private key -or a new private key which automatically generates a new certificate. +or a new private key which automatically generates a new certificate.
  • @@ -341,7 +341,7 @@ keychain add-certificates
  • -Update the Xcode project settings to use fetched code signing profiles: +Update the Xcode project settings to use fetched code signing profiles: ```bash xcode-project use-profiles @@ -392,7 +392,7 @@ INSTALLER_CERT_NAME=$(keychain list-certificates \ | .common_name][0]' \ | xargs) xcrun productsign --sign "$INSTALLER_CERT_NAME" unsigned.pkg "$PACKAGE_NAME" -rm -f unsigned.pkg +rm -f unsigned.pkg ```
  • @@ -436,7 +436,7 @@ covers releasing your build on TestFlight. ## Distribute to registered devices -See [distribution guide][distributionguide_macos] +See [distribution guide][distributionguide_macos] to prepare an archive for distribution to designated Mac computers. ## Release your app to the App Store @@ -468,9 +468,9 @@ detailed overview of the process of releasing an app to the App Store. ## Additional resources -To learn how to package and distribute your Flutter desktop app -for macOS the open source way, without using a paid Apple developer -account, check out the step-by-step +To learn how to package and distribute your Flutter desktop app +for macOS the open source way, without using a paid Apple developer +account, check out the step-by-step [macOS packaging guide][macos_packaging_guide]. [appicon]: {{site.apple-dev}}/design/human-interface-guidelines/macos/icons-and-images/app-icon/ diff --git a/src/content/deployment/obfuscate.md b/src/content/deployment/obfuscate.md index 08e3787e76b..4e0816c9eff 100644 --- a/src/content/deployment/obfuscate.md +++ b/src/content/deployment/obfuscate.md @@ -82,8 +82,8 @@ app in the future, you will need the symbol map. generate a SYMBOLS file: ```console - $ flutter build \ - --obfuscate \ + $ flutter build \ + --obfuscate \ --split-debug-info=/ ``` diff --git a/src/content/deployment/web.md b/src/content/deployment/web.md index b0a301b87fd..281cb702d4c 100644 --- a/src/content/deployment/web.md +++ b/src/content/deployment/web.md @@ -21,7 +21,7 @@ of your app and covers the following topics: ## Building the app for release -Build the app for deployment using the `flutter build web` command. +Build the app for deployment using the `flutter build web` command. ```console flutter build web diff --git a/src/content/embedded/index.md b/src/content/embedded/index.md index 5498456cc42..2070cd3ae3d 100644 --- a/src/content/embedded/index.md +++ b/src/content/embedded/index.md @@ -16,7 +16,7 @@ you might embed Flutter in the following situations: or a new operating system. The ability to embed Flutter, while stable, -uses low-level API and is _not_ for beginners. +uses low-level API and is _not_ for beginners. In addition to the resources listed below, you might consider joining [Discord][], where Flutter developers (including Google engineers) discuss @@ -44,5 +44,3 @@ resources. [Flutter Embedder Engine GLFW example]: {{site.repo.flutter}}/tree/main/engine/src/flutter/examples/glfw#flutter-embedder-engine-glfw-example [embedding Flutter in a terminal]: https://github.com/jiahaog/flt [Issue 31043]: {{site.repo.flutter}}/issues/31043 - - diff --git a/src/content/get-started/flutter-for/android-devs.md b/src/content/get-started/flutter-for/android-devs.md index 23bd3bc9585..b7a65dcdbff 100644 --- a/src/content/get-started/flutter-for/android-devs.md +++ b/src/content/get-started/flutter-for/android-devs.md @@ -12,8 +12,8 @@ can use this document as a jump start to Flutter development. :::note Android has two native user interface systems, Views (XML based) and Jetpack Compose. -Some fundamentals are shared so this document will provide value no matter what. -However, if you are coming from Jetpack Compose, +Some fundamentals are shared so this document will provide value no matter what. +However, if you are coming from Jetpack Compose, check out [Flutter for Jetpack Compose devs][] for detailed information about Jetpack Compose and how samples match up to Flutter examples. @@ -1582,7 +1582,7 @@ In Flutter there are two ways of adding touch listeners: ```dart class SampleTapApp extends StatelessWidget { const SampleTapApp({super.key}); - + @override Widget build(BuildContext context) { return Scaffold( @@ -2264,19 +2264,19 @@ class SampleApp extends StatelessWidget { ## Homescreen widgets -### How do I create a homescreen widget? +### How do I create a homescreen widget? -Android homescreen widgets cannot be created fully using Flutter. They must +Android homescreen widgets cannot be created fully using Flutter. They must use either Jetpack Glance(preferred method) or XML layout code. Using -the third-party package, [home_widget][], you can wire a homescreen widget +the third-party package, [home_widget][], you can wire a homescreen widget to Dart code, embed a Flutter component (as an image) in a host widget, and share data to/from Flutter to the homescreen widget. -To provide a richer and more engaging experience, it's recommended to add -widget previews to include in the widget picker. For devices running -Android 15 and above, generated widget previews allowing the user to see -a dynamic and personalized version of the target widget, giving them a -glimpse of how it will accurately on their home screen. For more information +To provide a richer and more engaging experience, it's recommended to add +widget previews to include in the widget picker. For devices running +Android 15 and above, generated widget previews allowing the user to see +a dynamic and personalized version of the target widget, giving them a +glimpse of how it will accurately on their home screen. For more information about the Generated Widget Previews and the fallback options for older devices, check the [Add generated previews to your widget picker][] documentation page. diff --git a/src/content/get-started/flutter-for/compose-devs.md b/src/content/get-started/flutter-for/compose-devs.md index 54f88c35a5c..c50c9c60e89 100644 --- a/src/content/get-started/flutter-for/compose-devs.md +++ b/src/content/get-started/flutter-for/compose-devs.md @@ -32,7 +32,7 @@ you can open and run some of the examples on DartPad. Flutter and Jetpack Compose code describe how the UI looks and works. Developers call this type of code a _declarative framework_. -While there are key differences especially when it comes to +While there are key differences especially when it comes to interacting with legacy Android code, there are many commonalities between the two frameworks. @@ -43,7 +43,7 @@ later noted in this document as _composables_. Composables can be altered or decorated through the use of _Modifier_ objects. ``` kotlin -Text("Hello, World!", +Text("Hello, World!", modifier: Modifier.padding(10.dp) ) Text("Hello, World!", @@ -73,18 +73,18 @@ Jetpack Compose nests `Composables` while Flutter nests `Widgets`. ### Layout process Jetpack Compose and Flutter handle layout in similar ways. Both of them -lay out the UI in a single pass and parent elements provide layout constraints +lay out the UI in a single pass and parent elements provide layout constraints down to their children. More specifically, -1. The parent measures itself and its children recursively providing +1. The parent measures itself and its children recursively providing any constraints from the parent to the child. -2. The children try to size themselves using the above methods and +2. The children try to size themselves using the above methods and provide their own children both their constraints and any that might apply from their ancestor nodes. 3. Upon encountering a leaf node (a node with no children), the size -and properties are determined based on the provided constraints +and properties are determined based on the provided constraints and the element is placed in the UI. -4. With all the children sized and placed, the root nodes can +4. With all the children sized and placed, the root nodes can determine their measurement, size, and placement. In both Jetpack Compose and Flutter, the parent component can override @@ -123,9 +123,9 @@ create buttons, react to on-press events, display lists, grids, and more. ### Getting started -For **Compose** apps, your main entry point will -be _Activity_ or one of its descendants, -generally _ComponentActivity_. +For **Compose** apps, your main entry point will +be _Activity_ or one of its descendants, +generally _ComponentActivity_. ```kotlin class MainActivity : ComponentActivity() { @@ -258,7 +258,7 @@ Column(verticalArrangement = Arrangement.Center) { } ``` -**Flutter** uses [`Row`][] and [`Column`][] as well but there are some slight differences for specifying child +**Flutter** uses [`Row`][] and [`Column`][] as well but there are some slight differences for specifying child widgets and alignment. The following is equivalent to the Compose example. ```dart @@ -287,23 +287,23 @@ center of the main axis. For `Row`, the main axis is the horizontal axis, inversely for `Column`, the main axis is the vertical axis. ::: note -Whereas Flutter's `Row` and `Column` have `MainAxisAlignment` +Whereas Flutter's `Row` and `Column` have `MainAxisAlignment` and `CrossAxisAlignment` to control how items are placed, the properties that control placement in Jetpack Compose are one vertical and horizontal property from the following: `verticalArrangement`, `verticalAlignment`, `horizontalAlignment`, and `horizontalArrangement`. The trick to determine -which is the `MainAxis` is to look for the property that ends in `arrangement`. +which is the `MainAxis` is to look for the property that ends in `arrangement`. The `CrossAxis` will be the property that ends in `alignment`. ::: ### Displaying a list view In **Compose**, you have a couple ways to create a list based on -the size of the list you need to display. For a small number of items -that can all be displayed at once, you can iterate over a collection +the size of the list you need to display. For a small number of items +that can all be displayed at once, you can iterate over a collection inside a `Column` or `Row`. -For a list with a large number of items, `LazyList` has better +For a list with a large number of items, `LazyList` has better performance. It only lays out the components that will be visible versus all of them. @@ -386,11 +386,11 @@ almost any widget that represents your data. ### Displaying a grid -Constructing a grid in **Compose** is similar to a +Constructing a grid in **Compose** is similar to a LazyList (`LazyColumn` or `LazyRow`). You can use the -same `items` closure. There are properties on each +same `items` closure. There are properties on each grid type to specify how to arrange the items, -whether or not to use adaptive or fixed layout, +whether or not to use adaptive or fixed layout, amongst others. @@ -423,7 +423,7 @@ This widget has various constructors. Each constructor has a similar goal, but uses different input parameters. The following example uses the `.builder()` initializer: -```dart +```dart const widgets = [ Text('Row 1'), Icon(Icons.arrow_downward), @@ -457,14 +457,14 @@ various parameters that the grid uses to lay out its components. This includes `crossAxisCount` that dictates the number of items displayed on each row. -Jetpack Compose's `LazyHorizontalGrid`, `LazyVerticalGrid`, and Flutter's `GridView` are somewhat +Jetpack Compose's `LazyHorizontalGrid`, `LazyVerticalGrid`, and Flutter's `GridView` are somewhat similar. `GridView` uses a delegate to decide how the grid should lay out its components. The `rows`, `columns`, and other associated properties on `LazyHorizontalGrid` \ `LazyVerticalGrid` serve the same purpose. ### Creating a scroll view -`LazyColumn` and `LazyRow` in **Jetpack Compose** have built-in +`LazyColumn` and `LazyRow` in **Jetpack Compose** have built-in support for scrolling. To create a scrolling view, **Flutter** uses [`SingleChildScrollView`][]. @@ -487,7 +487,7 @@ SingleChildScrollView( ### Responsive and adaptive design -Adaptive Design in **Compose** is a complex topic with many +Adaptive Design in **Compose** is a complex topic with many viable solutions: * Using a custom layout * Using `WindowSizeClass` alone @@ -497,7 +497,7 @@ available space along with specialized composable layouts for common layouts For that reason, you are encouraged to look into the **Flutter** -options directly and see what fits your requirements versus +options directly and see what fits your requirements versus attempting to find something that is a one to one translation. To create relative views in **Flutter**, you can use one of two options: @@ -580,7 +580,7 @@ To learn more ways to manage state, check out [State management][]. ### Drawing on the Screen -In **Compose**, you use the `Canvas` composable to draw +In **Compose**, you use the `Canvas` composable to draw shapes, images, and text to the screen. **Flutter** has an API based on the `Canvas` class, @@ -588,7 +588,7 @@ with two classes that help you draw: 1. [`CustomPaint`][] that requires a painter: - ```dart + ```dart CustomPaint( painter: SignaturePainter(_points), size: Size.infinite, @@ -600,9 +600,9 @@ with two classes that help you draw: ```dart class SignaturePainter extends CustomPainter { SignaturePainter(this.points); - + final List points; - + @override void paint(Canvas canvas, Size size) { final Paint paint = Paint() @@ -615,7 +615,7 @@ with two classes that help you draw: } } } - + @override bool shouldRepaint(SignaturePainter oldDelegate) => oldDelegate.points != points; @@ -631,8 +631,8 @@ and more. This section covers how to style your apps. ### Using dark mode -In **Compose**, you can control light and dark at any -arbitrary level by wrapping a component with +In **Compose**, you can control light and dark at any +arbitrary level by wrapping a component with a `Theme` composable. In **Flutter**, you can control light and dark mode at the app-level. @@ -658,10 +658,10 @@ Text("Hello, world!", color = Color.Green, fontWeight = FontWeight.Bold, fontSize = 30.sp) ``` ```kotlin -Text("Hello, world!", +Text("Hello, world!", style = TextStyle( - color = Color.Green, - fontSize = 30.sp, + color = Color.Green, + fontSize = 30.sp, fontWeight = FontWeight.Bold ), ) @@ -683,7 +683,7 @@ Text( ### Styling buttons -In **Compose**, you modify the colors of a button using +In **Compose**, you modify the colors of a button using the `colors` property. If left unmodified, they use the defaults from the current theme. @@ -696,7 +696,7 @@ Button(onClick = {}, } ``` -To style button widgets in **Flutter**, you similarly +To style button widgets in **Flutter**, you similarly set the style of its child, or modify properties on the button itself. ```dart @@ -718,9 +718,9 @@ FilledButton( There is commonly a need to bundle resources for use in your application. They can be animations, vector graphics, images, fonts, or other general files. -Unlike native Android apps that expect a set directory structure under `/res//` +Unlike native Android apps that expect a set directory structure under `/res//` where the qualifier could be indicating the type of file, a specific orientation, -or android version, Flutter doesn't require a specific location as long +or android version, Flutter doesn't require a specific location as long as the referenced files are listed in the `pubspec.yaml` file. Below is an excerpt from a `pubspec.yaml` referencing several images and a font file. @@ -771,7 +771,7 @@ Text( ### Using a font provider (Google Fonts) -One point of difference is using fonts from a font provider like Google Fonts. In **Compose**, +One point of difference is using fonts from a font provider like Google Fonts. In **Compose**, the instantiation is done inline with the same approximate code to reference a local file. After instantiating a provider that references the special strings for the font service, @@ -805,7 +805,7 @@ import 'package:google_fonts/google_fonts.dart'; Text( 'Flutter', style: GoogleFonts.firaSans(), - // or + // or //style: GoogleFonts.getFont('FiraSans') ), ``` diff --git a/src/content/get-started/flutter-for/dart-swift-concurrency.md b/src/content/get-started/flutter-for/dart-swift-concurrency.md index 22744f06ec9..628b500619d 100644 --- a/src/content/get-started/flutter-for/dart-swift-concurrency.md +++ b/src/content/get-started/flutter-for/dart-swift-concurrency.md @@ -6,75 +6,75 @@ description: > -Both Dart and Swift support concurrent programming. +Both Dart and Swift support concurrent programming. This guide should help you understand how concurrency works in Dart and how it compares to Swift. With this understanding, you can create -high-performing iOS apps. +high-performing iOS apps. -When developing in the Apple ecosystem, -some tasks might take a long time to complete. +When developing in the Apple ecosystem, +some tasks might take a long time to complete. These tasks include fetching or processing large amounts of data. iOS developers typically use Grand Central Dispatch (GCD) to schedule tasks using a shared thread pool. With GCD, developers add tasks to dispatch queues and GCD decides on which thread to execute them. -But, GCD spins up threads to +But, GCD spins up threads to handle remaining work items. -This means you can end up with a large number of threads +This means you can end up with a large number of threads and the system can become over committed. -With Swift, the structured concurrency model reduced the number -of threads and context switches. +With Swift, the structured concurrency model reduced the number +of threads and context switches. Now, each core has only one thread. -Dart has a single-threaded execution model, -with support for `Isolates`, an event loop, and asynchronous code. +Dart has a single-threaded execution model, +with support for `Isolates`, an event loop, and asynchronous code. An `Isolate` is Dart's implementation of a lightweight thread. -Unless you spawn an `Isolate`, your Dart code runs in the -main UI thread driven by an event loop. -Flutter's event loop is -equivalent to the iOS main loop—in other words, +Unless you spawn an `Isolate`, your Dart code runs in the +main UI thread driven by an event loop. +Flutter's event loop is +equivalent to the iOS main loop—in other words, the Looper attached to the main thread. -Dart's single-threaded model doesn't mean -you are required to run everything -as a blocking operation that causes the UI to freeze. -Instead, use the asynchronous -features that the Dart language provides, +Dart's single-threaded model doesn't mean +you are required to run everything +as a blocking operation that causes the UI to freeze. +Instead, use the asynchronous +features that the Dart language provides, such as `async`/`await`. ## Asynchronous Programming -An asynchronous operation allows other operations -to execute before it completes. -Both Dart and Swift support asynchronous functions -using the `async` and `await` keywords. -In both cases, `async` marks that a function -performs asynchronous work, -and `await` tells the system to await a result -from function. This means that the Dart VM _could_ -suspend the function, if necessary. +An asynchronous operation allows other operations +to execute before it completes. +Both Dart and Swift support asynchronous functions +using the `async` and `await` keywords. +In both cases, `async` marks that a function +performs asynchronous work, +and `await` tells the system to await a result +from function. This means that the Dart VM _could_ +suspend the function, if necessary. For more details on asynchronous programming, check out [Concurrency in Dart]({{site.dart-site}}/guides/language/concurrency). ### Leveraging the main thread/isolate -For Apple operating systems, the primary (also called the main) -thread is where the application begins running. -Rendering the user interface always happens on the main thread. -One difference between Swift and Dart is that -Swift might use different threads for different tasks, -and Swift doesn't guarantee which thread is used. -So, when dispatching UI updates in Swift, -you might need to ensure that the work occurs on the main thread. +For Apple operating systems, the primary (also called the main) +thread is where the application begins running. +Rendering the user interface always happens on the main thread. +One difference between Swift and Dart is that +Swift might use different threads for different tasks, +and Swift doesn't guarantee which thread is used. +So, when dispatching UI updates in Swift, +you might need to ensure that the work occurs on the main thread. -Say you want to write a function that fetches the -weather asynchronously and -displays the results. +Say you want to write a function that fetches the +weather asynchronously and +displays the results. -In GCD, to manually dispatch a process to the main thread, -you might do something like the following. +In GCD, to manually dispatch a process to the main thread, +you might do something like the following. First, define the `Weather` `enum`: @@ -84,11 +84,11 @@ enum Weather: String { } ``` -Next, define the view model and mark it as an [`@Observable`][] -that publishes the `result` of type `Weather?`. -Use GCD to create a background `DispatchQueue` to -send the work to the pool of threads, and then dispatch -back to the main thread to update the `result`. +Next, define the view model and mark it as an [`@Observable`][] +that publishes the `result` of type `Weather?`. +Use GCD to create a background `DispatchQueue` to +send the work to the pool of threads, and then dispatch +back to the main thread to update the `result`. ```swift @Observable class ContentViewModel { @@ -120,17 +120,17 @@ struct ContentView: View { } ``` -More recently, Swift introduced _actors_ to support -synchronization for shared, mutable state. +More recently, Swift introduced _actors_ to support +synchronization for shared, mutable state. To ensure that work is performed on the main thread, -define a view model class that is marked as a `@MainActor`, -with a `load()` function that internally calls an -asynchronous function using `Task`. +define a view model class that is marked as a `@MainActor`, +with a `load()` function that internally calls an +asynchronous function using `Task`. ```swift @MainActor @Observable class ContentViewModel { private(set) var result: Weather? - + func load() async { // Mimic 1 second network delay. try? await Task.sleep(nanoseconds: 1_000_000_000) @@ -139,7 +139,7 @@ asynchronous function using `Task`. } ``` -Next, define the view model as a state using `@State`, +Next, define the view model as a state using `@State`, with a `load()` function that can be called by the view model: ```swift @@ -154,8 +154,8 @@ struct ContentView: View { } ``` -In Dart, all work runs on the main isolate by default. -To implement the same example in Dart, +In Dart, all work runs on the main isolate by default. +To implement the same example in Dart, first, create the `Weather` `enum`: @@ -163,9 +163,9 @@ first, create the `Weather` `enum`: enum Weather { rainy, windy, sunny } ``` -Then, define a simple view model (similar to what was created in SwiftUI), +Then, define a simple view model (similar to what was created in SwiftUI), to fetch the weather. In Dart, a `Future` object represents a value to be -provided in the future. A `Future` is similar to Swift's `@Observable`. +provided in the future. A `Future` is similar to Swift's `@Observable`. In this example, a function within the view model returns a `Future` object: @@ -181,21 +181,21 @@ class HomePageViewModel { } ``` -The `load()` function in this example shares -similarities with the Swift code. +The `load()` function in this example shares +similarities with the Swift code. The Dart function is marked as `async` because it uses the `await` keyword. Additionally, a Dart function marked as `async` automatically returns a `Future`. -In other words, you don't have to create a -`Future` instance manually +In other words, you don't have to create a +`Future` instance manually inside functions marked as `async`. -For the last step, display the weather value. -In Flutter, [`FutureBuilder`]({{site.api}}/flutter/widgets/FutureBuilder-class.html) and -[`StreamBuilder`]({{site.api}}/flutter/widgets/StreamBuilder-class.html) -widgets are used to display the results of a Future in the UI. +For the last step, display the weather value. +In Flutter, [`FutureBuilder`]({{site.api}}/flutter/widgets/FutureBuilder-class.html) and +[`StreamBuilder`]({{site.api}}/flutter/widgets/StreamBuilder-class.html) +widgets are used to display the results of a Future in the UI. The following example uses a `FutureBuilder`: @@ -236,15 +236,15 @@ For the complete example, check out the ### Leveraging a background thread/isolate -Flutter apps can run on a variety of multi-core hardware, -including devices running macOS and iOS. -To improve the performance of these applications, +Flutter apps can run on a variety of multi-core hardware, +including devices running macOS and iOS. +To improve the performance of these applications, you must sometimes run tasks on different cores -concurrently. This is especially important -to avoid blocking UI rendering with long-running operations. +concurrently. This is especially important +to avoid blocking UI rendering with long-running operations. In Swift, you can leverage GCD to run tasks on global queues -with different quality of service class (qos) properties. +with different quality of service class (qos) properties. This indicates the task's priority. ```swift @@ -259,11 +259,11 @@ func parse(string: String, completion: @escaping ([String:Any]) -> Void) { } ``` -In Dart, you can offload computation to a worker isolate, -often called a background worker. -A common scenario spawns a simple worker isolate and -returns the results in a message when the worker exits. -As of Dart 2.19, you can use `Isolate.run()` to +In Dart, you can offload computation to a worker isolate, +often called a background worker. +A common scenario spawns a simple worker isolate and +returns the results in a message when the worker exits. +As of Dart 2.19, you can use `Isolate.run()` to spawn an isolate and run computations: ```dart @@ -276,7 +276,7 @@ void main() async { } ``` -In Flutter, you can also use the `compute` function +In Flutter, you can also use the `compute` function to spin up an isolate to run a callback function: ```dart diff --git a/src/content/get-started/flutter-for/declarative.md b/src/content/get-started/flutter-for/declarative.md index dc6d19dd56f..06547c83efb 100644 --- a/src/content/get-started/flutter-for/declarative.md +++ b/src/content/get-started/flutter-for/declarative.md @@ -64,4 +64,3 @@ state of the layout) behind the scenes with RenderObjects. RenderObjects persist between frames and Flutter's lightweight Widgets tell the framework to mutate the RenderObjects between states. The Flutter framework handles the rest. - diff --git a/src/content/get-started/flutter-for/swiftui-devs.md b/src/content/get-started/flutter-for/swiftui-devs.md index fc4092bf72a..368a4965dc8 100644 --- a/src/content/get-started/flutter-for/swiftui-devs.md +++ b/src/content/get-started/flutter-for/swiftui-devs.md @@ -685,9 +685,9 @@ with two classes that help you draw: ```dart dartpad="978d64ee66d54177fb639f8a9f801039" class SignaturePainter extends CustomPainter { SignaturePainter(this.points); - + final List points; - + @override void paint(Canvas canvas, Size size) { final Paint paint = Paint() @@ -700,7 +700,7 @@ with two classes that help you draw: } } } - + @override bool shouldRepaint(SignaturePainter oldDelegate) => oldDelegate.points != points; @@ -752,10 +752,10 @@ call your navigation routes using their names. // Defines the route name as a constant // so that it's reusable. const detailsPageRouteName = '/details'; - + class App extends StatelessWidget { const App({super.key}); - + @override Widget build(BuildContext context) { return CupertinoApp( @@ -806,7 +806,7 @@ call your navigation routes using their names. ```dart dartpad="d8b22d4dcbefdc8a2e21f1382cf7dc2a" class DetailsPage extends StatelessWidget { const DetailsPage({super.key}); - + @override Widget build(BuildContext context) { // Read the person instance from the arguments. diff --git a/src/content/get-started/flutter-for/uikit-devs.md b/src/content/get-started/flutter-for/uikit-devs.md index 1da231fad6e..9308cf6e20e 100644 --- a/src/content/get-started/flutter-for/uikit-devs.md +++ b/src/content/get-started/flutter-for/uikit-devs.md @@ -1131,7 +1131,7 @@ In Flutter, there are two ways of adding touch listeners: ```dart class SampleTapApp extends StatelessWidget { const SampleTapApp({super.key}); - + @override Widget build(BuildContext context) { return Scaffold( diff --git a/src/content/get-started/flutter-for/web-devs.md b/src/content/get-started/flutter-for/web-devs.md index 1909b474942..aa4022437b7 100644 --- a/src/content/get-started/flutter-for/web-devs.md +++ b/src/content/get-started/flutter-for/web-devs.md @@ -13,7 +13,7 @@ It maps HTML/CSS code snippets to their Flutter/Dart code equivalents. Flutter is a framework for building cross-platform applications that uses the Dart programming language. To understand some differences between programming with Dart -and programming with Javascript, +and programming with Javascript, see [Learning Dart as a JavaScript Developer][]. One of the fundamental differences between @@ -996,4 +996,3 @@ final container = Container( [`TextStyle`]: {{site.api}}/flutter/painting/TextStyle-class.html [`Transform`]: {{site.api}}/flutter/widgets/Transform-class.html [Understanding constraints]: /ui/layout/constraints - diff --git a/src/content/get-started/flutter-for/xamarin-forms-devs.md b/src/content/get-started/flutter-for/xamarin-forms-devs.md index f2f3ab6c76c..223a22db563 100644 --- a/src/content/get-started/flutter-for/xamarin-forms-devs.md +++ b/src/content/get-started/flutter-for/xamarin-forms-devs.md @@ -1627,7 +1627,7 @@ In Flutter there are two very similar ways: ```dart class SampleApp extends StatelessWidget { const SampleApp({super.key}); - + @override Widget build(BuildContext context) { return Scaffold( diff --git a/src/content/get-started/fundamentals/dart.md b/src/content/get-started/fundamentals/dart.md index b7309afd190..0be1aa7bdea 100644 --- a/src/content/get-started/fundamentals/dart.md +++ b/src/content/get-started/fundamentals/dart.md @@ -9,14 +9,14 @@ next: path: /get-started/fundamentals/widgets --- -To get started with Flutter, -you need to have some familiarity with -the Dart programming language, which Flutter +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 +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 @@ -24,19 +24,19 @@ continue with this series. 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. +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, +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 @@ -45,7 +45,7 @@ import 'package:http/http.dart' as http; class Package { final String name; - final String latestVersion; + final String latestVersion; final String? description; Package(this.name, this.latestVersion, {this.description}); @@ -73,44 +73,44 @@ void main() async { } ``` -This program has two parts: -the `Package` class declaration, and the business logic, +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, +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 +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, +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 +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, +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. @@ -123,7 +123,7 @@ like the following: ```dart title="lib/main.dart" void main() { runApp(const MyApp()); -} +} ``` Perform any _quick_ initialization (less than a frame or two) @@ -149,8 +149,8 @@ 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 +If these features aren't familiar to you, +you can find resources to learn Dart on the [Bootstrap into Dart][] page. ## Next: Widgets @@ -158,10 +158,10 @@ you can find resources to learn Dart on the 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, +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 +In the next section, you'll learn about the building block of Flutter apps: widgets. [Asynchronous programming]: {{site.dart-site}}/libraries/async/async-await @@ -177,7 +177,7 @@ building block of Flutter apps: widgets. ## Feedback -As this section of the website is evolving, +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/local-caching.md b/src/content/get-started/fundamentals/local-caching.md index 81cb240ab4d..9bcc83d593e 100644 --- a/src/content/get-started/fundamentals/local-caching.md +++ b/src/content/get-started/fundamentals/local-caching.md @@ -59,7 +59,7 @@ defined and explained below. ## Risks of caching data -An app is said to have a **stale cache** when the data within +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. @@ -107,7 +107,7 @@ Its implementation might look like this: ```dart class UserRepository { UserRepository(this.api); - + final Api api; final Map _userCache = {}; @@ -233,7 +233,7 @@ 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 +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: @@ -245,7 +245,7 @@ and iOS, see the following documentation: ## Feedback -As this section of the website is evolving, +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 index 73fa3510716..717f4db488b 100644 --- a/src/content/get-started/fundamentals/networking.md +++ b/src/content/get-started/fundamentals/networking.md @@ -39,7 +39,7 @@ 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. +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 @@ -59,13 +59,13 @@ 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. +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][]. +of the [`freezed` package][]. The second links to a codelab that covers patterns -and records using a case study of parsing JSON. +and records using a case study of parsing JSON. * YouTube video: [Freezed (Package of the Week)][] * Codelab: [Dive into Dart's patterns and records][] diff --git a/src/content/get-started/fundamentals/widgets.md b/src/content/get-started/fundamentals/widgets.md index 6d329e3dc83..3fe7aea5022 100644 --- a/src/content/get-started/fundamentals/widgets.md +++ b/src/content/get-started/fundamentals/widgets.md @@ -197,7 +197,7 @@ class _CounterWidgetState extends State { _counter++; }); } - + @override Widget build(BuildContext context) { return Text('$_counter'); diff --git a/src/content/get-started/learn-flutter.md b/src/content/get-started/learn-flutter.md index 082b1ebea05..c93b2cceec1 100644 --- a/src/content/get-started/learn-flutter.md +++ b/src/content/get-started/learn-flutter.md @@ -8,24 +8,24 @@ showToc: false ## For new Flutter developers -If you're brand new to Flutter, -we suggest you complete the following resources +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. + 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, + 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, + 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][] + 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 + the new Flutter developer. It guides you + through some of the most important pieces of building Flutter applications. ## Apply your existing knowledge @@ -43,7 +43,7 @@ in order: * [Flutter learning resources][] * [Flutter API Docs][] -Reach out to us on our [mailing list][]. +Reach out to us on our [mailing list][]. We'd love to hear from you! Happy Fluttering! diff --git a/src/content/jobs/_template.md b/src/content/jobs/_template.md index 0b2d8c7b12c..c1c5222097c 100644 --- a/src/content/jobs/_template.md +++ b/src/content/jobs/_template.md @@ -39,7 +39,7 @@ and are [committed to furthering our culture of inclusion](https://flutter.dev/c In addition to groups like the [Flutteristas](https://flutteristas.org/), [Employee Resource Groups (ERGs)](https://diversity.google/commitments/) are employee-initiated networks for supporting underrepresented employees -and their allies with shared values of creating belonging +and their allies with shared values of creating belonging across their communities and Google. ### Work-life balance diff --git a/src/content/jobs/tech_writer_ii.md b/src/content/jobs/tech_writer_ii.md index f3cde2024bd..c876594f1b4 100644 --- a/src/content/jobs/tech_writer_ii.md +++ b/src/content/jobs/tech_writer_ii.md @@ -55,7 +55,7 @@ and are [committed to furthering our culture of inclusion](https://flutter.dev/c In addition to groups like the [Flutteristas](https://flutteristas.org/), [Employee Resource Groups (ERGs)](https://diversity.google/commitments/) are employee-initiated networks for supporting underrepresented employees -and their allies with shared values of creating belonging +and their allies with shared values of creating belonging across their communities and Google. ### Work-Life balance diff --git a/src/content/packages-and-plugins/background-processes.md b/src/content/packages-and-plugins/background-processes.md index 6c7e46f320a..5ebe9ef3d98 100644 --- a/src/content/packages-and-plugins/background-processes.md +++ b/src/content/packages-and-plugins/background-processes.md @@ -16,8 +16,8 @@ in that it doesn't share memory with the main program. You'll set up your isolate for background execution using callbacks and a callback dispatcher. -Additionally, the [WorkManager] plugin enables persistent background processing -that keeps tasks scheduled through app restarts and system reboots. +Additionally, the [WorkManager] plugin enables persistent background processing +that keeps tasks scheduled through app restarts and system reboots. For more information and a geofencing example that uses background execution of Dart code, see the Medium article by Ben Konyi, @@ -25,6 +25,6 @@ execution of Dart code, see the Medium article by Ben Konyi, Geofencing][background-processes]. At the end of this article, you'll find links to example code, and relevant documentation for Dart, iOS, and Android. - + [background-processes]: {{site.flutter-medium}}/executing-dart-in-the-background-with-flutter-plugins-and-geofencing-2b3e40a1a124 -[WorkManager]: {{site.pub-pkg}}/workmanager +[WorkManager]: {{site.pub-pkg}}/workmanager diff --git a/src/content/packages-and-plugins/using-packages.md b/src/content/packages-and-plugins/using-packages.md index 5cd0e6e1776..425c4aac4d1 100644 --- a/src/content/packages-and-plugins/using-packages.md +++ b/src/content/packages-and-plugins/using-packages.md @@ -313,7 +313,7 @@ additional dependency options are available: dependencies: packageA: path: ../packageA/ - + ``` **Git dependency** @@ -392,23 +392,23 @@ To use this package: ```dart import 'package:css_colors/css_colors.dart'; import 'package:flutter/material.dart'; - + void main() { runApp(const MyApp()); } - + class MyApp extends StatelessWidget { const MyApp({super.key}); - + @override Widget build(BuildContext context) { return const MaterialApp(home: DemoPage()); } } - + class DemoPage extends StatelessWidget { const DemoPage({super.key}); - + @override Widget build(BuildContext context) { return Scaffold(body: Container(color: CSSColors.orange)); @@ -453,27 +453,27 @@ To use this plugin: ```dart import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; - + void main() { runApp(const MyApp()); } - + class MyApp extends StatelessWidget { const MyApp({super.key}); - + @override Widget build(BuildContext context) { return const MaterialApp(home: DemoPage()); } } - + class DemoPage extends StatelessWidget { const DemoPage({super.key}); - + void launchURL() { launchUrl(Uri.parse('https://flutter.dev')); } - + @override Widget build(BuildContext context) { return Scaffold( diff --git a/src/content/perf/deferred-components.md b/src/content/perf/deferred-components.md index 1854415f4c6..e6a22cdf024 100644 --- a/src/content/perf/deferred-components.md +++ b/src/content/perf/deferred-components.md @@ -146,7 +146,7 @@ to your app initialization: ```java import io.flutter.embedding.engine.dynamicfeatures.PlayStoreDeferredComponentManager; import io.flutter.FlutterInjector; -... +... PlayStoreDeferredComponentManager deferredComponentManager = new PlayStoreDeferredComponentManager(this, null); FlutterInjector.setInstance(new FlutterInjector.Builder() @@ -154,7 +154,7 @@ FlutterInjector.setInstance(new FlutterInjector.Builder() ``` - +
  • Opt into deferred components by adding diff --git a/src/content/perf/impeller.md b/src/content/perf/impeller.md index 216bd0c80ce..a748eca7395 100644 --- a/src/content/perf/impeller.md +++ b/src/content/perf/impeller.md @@ -158,4 +158,4 @@ check out the [README.md][] file in the source tree. [impeller-metal-validation]: {{site.repo.flutter}}/blob/main/docs/engine/impeller/docs/metal_validation.md [impeller-ubo-gles2]: {{site.repo.flutter}}/blob/main/docs/engine/impeller/docs/ubo_gles2.md [impeller-shader-optimization]: {{site.repo.flutter}}/blob/main/docs/engine/impeller/docs/shader_optimization.md -[impeller-blending]: {{site.repo.flutter}}/blob/main/docs/engine/impeller/docs/blending.md +[impeller-blending]: {{site.repo.flutter}}/blob/main/docs/engine/impeller/docs/blending.md diff --git a/src/content/perf/isolates.md b/src/content/perf/isolates.md index 382539cf4c1..2b843d79433 100644 --- a/src/content/perf/isolates.md +++ b/src/content/perf/isolates.md @@ -343,7 +343,7 @@ For more information on isolates, check out the following resources: or the pub package that clones the functionality for Dart applications not using Flutter. - Dart's Isolates are an implementation of the [Actor model][]. -- [isolate_agents][] is a package that abstracts Ports and make it easier to create long-lived isolates. +- [isolate_agents][] is a package that abstracts Ports and make it easier to create long-lived isolates. - Read more about the `BackgroundIsolateBinaryMessenger` API [announcement][]. [announcement]: {{site.flutter-medium}}/introducing-background-isolate-channels-7a299609cad8 diff --git a/src/content/perf/metrics.md b/src/content/perf/metrics.md index 039dc586aad..7a54261a1dd 100644 --- a/src/content/perf/metrics.md +++ b/src/content/perf/metrics.md @@ -5,7 +5,7 @@ description: Flutter metrics, and which tools and APIs are used to get them * Startup time to the first frame * Check the time when - [WidgetsBinding.instance.firstFrameRasterized][firstFrameRasterized] + [WidgetsBinding.instance.firstFrameRasterized][firstFrameRasterized] is true. * See the [perf dashboard](https://flutter-flutter-perf.skia.org/e/?queries=sub_result%3DtimeToFirstFrameRasterizedMicros). @@ -17,13 +17,13 @@ description: Flutter metrics, and which tools and APIs are used to get them * Statistics of frame `buildDuration` (`*_frame_build_time_millis`) * We recommend monitoring four stats: average, 90th percentile, 99th percentile, and worst frame build time. - * See, for example, [metrics][transition_build] for the + * See, for example, [metrics][transition_build] for the `flutter_gallery__transition_perf` test. * Statistics of frame `rasterDuration` (`*_frame_build_time_millis`) * We recommend monitoring four stats: average, 90th percentile, 99th percentile, and worst frame build time. - * See, for example, [metrics][transition_raster] for the + * See, for example, [metrics][transition_raster] for the `flutter_gallery__transition_perf` test. * CPU/GPU usage (a good approximation for energy use) @@ -39,8 +39,8 @@ description: Flutter metrics, and which tools and APIs are used to get them * For info on how to measure the size more accurately, see the [app size](/perf/app-size) page. -For a complete list of performance metrics Flutter measures per commit, visit -the following sites, click **Query**, and filter the **test** and +For a complete list of performance metrics Flutter measures per commit, visit +the following sites, click **Query**, and filter the **test** and **sub_result** fields: * [https://flutter-flutter-perf.skia.org/e/](https://flutter-flutter-perf.skia.org/e/) diff --git a/src/content/perf/ui-performance.md b/src/content/perf/ui-performance.md index f7ea27e327f..1e5dabbd6b3 100644 --- a/src/content/perf/ui-performance.md +++ b/src/content/perf/ui-performance.md @@ -180,8 +180,8 @@ represent the current frame. ### Review the graphs {:#interpreting-the-graphs} -The top graph (marked "GPU") shows the time spent by -the raster thread, the bottom one graph shows the time +The top graph (marked "GPU") shows the time spent by +the raster thread, the bottom one graph shows the time spent by the UI thread. The white lines across the graphs show 16ms increments along the vertical axis; if the graph ever goes over one @@ -248,7 +248,7 @@ on other threads. : Performs expensive tasks (mostly I/O) that would otherwise block either the UI or raster threads. _This thread is not shown in the performance overlay._ - + For links to more information and videos, see [The Framework architecture][] in the [Flutter wiki][], and the community article, diff --git a/src/content/perf/web-performance.md b/src/content/perf/web-performance.md index 60ab04e28cc..52f707708eb 100644 --- a/src/content/perf/web-performance.md +++ b/src/content/perf/web-performance.md @@ -9,7 +9,7 @@ Profiling Flutter web apps requires Flutter version 3.14 or later. The Flutter framework emits timeline events as it works to build frames, draw scenes, and track other activity such as garbage collections. -These events are exposed in the +These events are exposed in the [Chrome DevTools performance panel][] for debugging. :::note diff --git a/src/content/platform-integration/android/call-jetpack-apis.md b/src/content/platform-integration/android/call-jetpack-apis.md index ebcad1922c6..d2476b9145d 100644 --- a/src/content/platform-integration/android/call-jetpack-apis.md +++ b/src/content/platform-integration/android/call-jetpack-apis.md @@ -44,7 +44,7 @@ support you need. The next sections describe two different ways to call native code from Dart. :::note -Neither solution below is inherently better or worse than +Neither solution below is inherently better or worse than existing plugins, because all plugins use one of the following two options. ::: @@ -53,7 +53,7 @@ two options. The most direct and efficient way to invoke native APIs is by calling the API directly, via FFI. This links your Dart executable -to any specified native code at compile-time, allowing you to +to any specified native code at compile-time, allowing you to call it directly from the UI thread through a small amount of glue code. In most cases, [ffigen][ffigen] or [jnigen][jnigen] are helpful in writing this glue code. @@ -80,7 +80,7 @@ MethodChannels are always asynchronous, which might or might not matter to you, depending on your use case. As with FFI and direct calls to native code, using a `MethodChannel` requires a small amount of glue code to translate your Dart objects -into native objects, and then back again. In most cases, +into native objects, and then back again. In most cases, [`pkg:pigeon`][pigeon] is helpful in writing this glue code. For complete guidance on adding MethodChannels to your Flutter diff --git a/src/content/platform-integration/android/compose-activity.md b/src/content/platform-integration/android/compose-activity.md index 38a13b23bda..b1424288bfe 100644 --- a/src/content/platform-integration/android/compose-activity.md +++ b/src/content/platform-integration/android/compose-activity.md @@ -137,7 +137,7 @@ The first file requiring modifications is `android/app/build.gradle`. [developer.android.com]: {{site.android-dev}}/jetpack/androidx/releases/compose-kotlin 2. Next, add the following block at the bottom of the file, at the root level: - + ```groovy title="android/app/build.gradle" dependencies { implementation("androidx.core:core-ktx:1.10.1") @@ -160,9 +160,9 @@ The first file requiring modifications is `android/app/build.gradle`. ``` The second file requiring modifications is `android/build.gradle`. - + 1. Add the following buildscript block at the top of the file: - + ```groovy title="android/build.gradle" buildscript { dependencies { @@ -178,9 +178,9 @@ The first file requiring modifications is `android/app/build.gradle`. The third file requiring modifications is `android/app/src/main/AndroidManifest.xml`. - + 1. In the root application block, add the following `` declaration: - + ```xml title="android/app/src/main/AndroidManifest.xml" creationParams = {}; - + return PlatformViewLink( viewType: viewType, surfaceFactory: (context, controller) { @@ -148,7 +148,7 @@ use the following instructions: const String viewType = ''; // Pass parameters to the platform side. final Map creationParams = {}; - + return AndroidView( viewType: viewType, layoutDirection: TextDirection.ltr, diff --git a/src/content/platform-integration/android/predictive-back.md b/src/content/platform-integration/android/predictive-back.md index 934bf947bbf..11766c4fc9b 100644 --- a/src/content/platform-integration/android/predictive-back.md +++ b/src/content/platform-integration/android/predictive-back.md @@ -59,4 +59,3 @@ You can find more information at the following link: * [Android predictive back][] breaking change [Android predictive back]: /release/breaking-changes/android-predictive-back - diff --git a/src/content/platform-integration/android/restore-state-android.md b/src/content/platform-integration/android/restore-state-android.md index 9b71efc83d4..762bbed8de8 100644 --- a/src/content/platform-integration/android/restore-state-android.md +++ b/src/content/platform-integration/android/restore-state-android.md @@ -1,4 +1,4 @@ ---- +--- title: "Restore state on Android" description: "How to restore the state of your Android app after it's been killed by the OS." --- @@ -63,7 +63,7 @@ You can enable state restoration with just a few tasks: and hold that state in a [`RestorableProperty`][]. (The Flutter API provides various subclasses for different data types.) - Define those `RestorableProperty` widgets + Define those `RestorableProperty` widgets in a `State` class that uses the [`RestorationMixin`][]. Register those widgets with the mixin in a `restoreState` method. diff --git a/src/content/platform-integration/ios/ios-debugging.md b/src/content/platform-integration/ios/ios-debugging.md index cd387f4e015..cf452f94847 100644 --- a/src/content/platform-integration/ios/ios-debugging.md +++ b/src/content/platform-integration/ios/ios-debugging.md @@ -17,4 +17,3 @@ permission by enabling **Settings > Privacy > Local Network > Your App**. [local network permissions in iOS 14 or later]: {{site.apple-dev}}/news/?id=0oi77447 - diff --git a/src/content/platform-integration/ios/restore-state-ios.md b/src/content/platform-integration/ios/restore-state-ios.md index 89ca8400b86..744b0334b66 100644 --- a/src/content/platform-integration/ios/restore-state-ios.md +++ b/src/content/platform-integration/ios/restore-state-ios.md @@ -1,4 +1,4 @@ ---- +--- title: "Restore state on iOS" description: "How to restore the state of your iOS app after it's been killed by the OS." --- @@ -10,7 +10,7 @@ often kills the backgrounded app to release memory or improve performance for the app running in the foreground. You can use the [`RestorationManager`][] (and related) -classes to handle state restoration. +classes to handle state restoration. An iOS app requires [a bit of extra setup][] in Xcode, but the restoration classes otherwise work the same on both iOS and Android. @@ -22,4 +22,3 @@ and the [VeggieSeasons][] code sample. [`RestorationManager`]: {{site.api}}/flutter/services/RestorationManager-class.html [State restoration on Android]: /platform-integration/android/restore-state-android [VeggieSeasons]: https://github.com/samples/demos/tree/main/veggieseasons - diff --git a/src/content/platform-integration/macos/platform-views.md b/src/content/platform-integration/macos/platform-views.md index 10ccd5abe20..1e7ffa547fd 100644 --- a/src/content/platform-integration/macos/platform-views.md +++ b/src/content/platform-integration/macos/platform-views.md @@ -40,7 +40,7 @@ To create a platform view on macOS, use the following instructions: On the Dart side, create a `Widget` and add the build implementation, as shown in the following steps: -In the Dart widget file, make changes similar to those +In the Dart widget file, make changes similar to those shown in `native_view_example.dart`: 1. Add the following imports: @@ -60,7 +60,7 @@ shown in `native_view_example.dart`: const String viewType = ''; // Pass parameters to the platform side. final Map creationParams = {}; - + return AppKitView( viewType: viewType, layoutDirection: TextDirection.ltr, diff --git a/src/content/platform-integration/web/building.md b/src/content/platform-integration/web/building.md index eaad690d422..6559bf96f37 100644 --- a/src/content/platform-integration/web/building.md +++ b/src/content/platform-integration/web/building.md @@ -191,4 +191,3 @@ running integration tests in a browser, see the [Integration testing][] page. [Web FAQ]: /platform-integration/web/faq [Web renderers]: /platform-integration/web/renderers [Widget tests]: /testing/overview#widget-tests - diff --git a/src/content/platform-integration/web/faq.md b/src/content/platform-integration/web/faq.md index 42faf7b28b5..b8a396bd6f2 100644 --- a/src/content/platform-integration/web/faq.md +++ b/src/content/platform-integration/web/faq.md @@ -111,7 +111,7 @@ Not currently. ### Why doesn't my app update immediately after it's deployed? -You might need to configure the `Cache-Control` header returned by your web server. +You might need to configure the `Cache-Control` header returned by your web server. For example, if this header is set to 3600, then the browser and CDN will cache the asset for 1 hour, and your users might see an out-of-date version of your app up to 1 hour after you deploy a new version. For @@ -119,7 +119,7 @@ more information about caching on the web, check out [Prevent unnecessary network requests with the HTTP Cache][http-cache]. It is a good idea to be aware of this behavior to avoid an undesirable user experience. -After you deploy your app, users might use a +After you deploy your app, users might use a cached version of your app (cached by the browser or CDN) for the duration defined by your cache headers. This can lead to users using a version of your app that diff --git a/src/content/platform-integration/web/renderers.md b/src/content/platform-integration/web/renderers.md index 25f4b509b5f..e0f8fd7e6ed 100644 --- a/src/content/platform-integration/web/renderers.md +++ b/src/content/platform-integration/web/renderers.md @@ -66,13 +66,13 @@ flutter build web --wasm ## Renderers Flutter has two renderers (`canvaskit` and `skwasm`) -that re-implement the Flutter engine to run the browser. +that re-implement the Flutter engine to run the browser. The renderer converts UI primitives (stored as `Scene` objects) into pixels. ### canvaskit -The `canvaskit` renderer is compatible with all modern browsers, and is the +The `canvaskit` renderer is compatible with all modern browsers, and is the renderer that is used in the _default_ build mode. It includes a copy of Skia compiled to WebAssembly, which adds @@ -147,7 +147,7 @@ any decisions about which renderer to use, must be made prior to calling To compile Dart to WebAssembly, your app and its plugins / packages must meet the following requirements: -- **Use new JS Interop** - +- **Use new JS Interop** - The code must only use the new JS interop library `dart:js_interop`. Old-style `dart:js`, `dart:js_util`, and `package:js` are no longer supported. - **Use new Web APIs** - diff --git a/src/content/platform-integration/web/wasm.md b/src/content/platform-integration/web/wasm.md index 41b7fabe770..1058ae34913 100644 --- a/src/content/platform-integration/web/wasm.md +++ b/src/content/platform-integration/web/wasm.md @@ -75,7 +75,7 @@ Even with the `--wasm` flag, Flutter will still compile the application to JavaScript. If WasmGC support is not detected at runtime, the JavaScript output is used so the application will continue to work in all major browsers. -You can verify whether the app is actually running with Wasm by checking for +You can verify whether the app is actually running with Wasm by checking for the `dart2wasm` environment variable, set during compilation (preferred). ```dart diff --git a/src/content/platform-integration/web/web-content-in-flutter.md b/src/content/platform-integration/web/web-content-in-flutter.md index a19d4dbea97..34497e23160 100644 --- a/src/content/platform-integration/web/web-content-in-flutter.md +++ b/src/content/platform-integration/web/web-content-in-flutter.md @@ -58,7 +58,7 @@ content that needs to be added to your app. The resulting code is more verbose, and has two steps per platform view type: 1. Register the HTML Element Factory using -`platformViewRegistry.registerViewFactory` provided by `dart:ui_web.` +`platformViewRegistry.registerViewFactory` provided by `dart:ui_web.` 2. Place the widget with the desired `viewType` with `HtmlElementView('viewType')` in your app's widget tree. diff --git a/src/content/platform-integration/web/web-dev-config-file.md b/src/content/platform-integration/web/web-dev-config-file.md index 5b560b28ee8..e5cdbf0f656 100644 --- a/src/content/platform-integration/web/web-dev-config-file.md +++ b/src/content/platform-integration/web/web-dev-config-file.md @@ -36,7 +36,7 @@ You can define the host, port, and HTTPS settings for your development server. server: host: "0.0.0.0" # Defines the binding address port: 8080 # Specifies the port for the development server - https: + https: cert-path: "/path/to/cert.pem" # Path to your TLS certificate cert-key-path: "/path/to/key.pem" # Path to TLS certificate key ``` diff --git a/src/content/platform-integration/web/web-images.md b/src/content/platform-integration/web/web-images.md index af74ad1e071..f5fd1b9a1a5 100644 --- a/src/content/platform-integration/web/web-images.md +++ b/src/content/platform-integration/web/web-images.md @@ -4,8 +4,8 @@ shortTitle: Web images description: Learn how to load and display images on the web. --- -The web supports the standard [`Image`][] widget and the more -advanced [`dart:ui/Image`][] class (where more fine-grained control +The web supports the standard [`Image`][] widget and the more +advanced [`dart:ui/Image`][] class (where more fine-grained control is needed to display images). However, because web browsers are built to run untrusted code safely, there are certain limitations in what you can do with images compared @@ -59,7 +59,7 @@ hardware-acceleration. one site accesses the resources of another site. It is designed such that, by default, one web-site is not allowed to make HTTP requests to another site using -[XHR][] or [`fetch`][]. +[XHR][] or [`fetch`][]. This prevents scripts on another site from acting on behalf of the user and from gaining access to another site's resources without permission. @@ -72,7 +72,7 @@ Therefore, images must only come from servers that have a CORS policy configured to work with the domain that serves your application. -:::note +:::note For more information about web renderers, see [Web renderers][]. ::: diff --git a/src/content/platform-integration/windows/building.md b/src/content/platform-integration/windows/building.md index 52dbd043453..4bc4081c661 100644 --- a/src/content/platform-integration/windows/building.md +++ b/src/content/platform-integration/windows/building.md @@ -280,11 +280,11 @@ In addition to that executable, you need the following: * `msvcp140.dll` * `vcruntime140.dll` * `vcruntime140_1.dll` - + Place the DLL files in the directory next to the executable and the other DLLs, and bundle them together in a zip file. The resulting structure looks something like this: - + ```plaintext Release │ flutter_windows.dll @@ -305,8 +305,8 @@ add this folder to a Windows installer such as Inno Setup, WiX, etc. ## Additional resources -To learn how to build an `.exe` using Inno Setup to distribute -your Flutter desktop app for Windows, check out the step-by-step +To learn how to build an `.exe` using Inno Setup to distribute +your Flutter desktop app for Windows, check out the step-by-step [Windows packaging guide][windows_packaging_guide]. [deployment example walkthroughs]: https://docs.microsoft.com/en-us/cpp/windows/deployment-examples diff --git a/src/content/reference/crash-reporting.md b/src/content/reference/crash-reporting.md index 647bac581ce..31584064597 100644 --- a/src/content/reference/crash-reporting.md +++ b/src/content/reference/crash-reporting.md @@ -15,7 +15,7 @@ A crash report might contain the following information: * The version of Flutter used to run the command. * The runtime type of the error, for example `StateError` or `NoSuchMethodError`. -* The stack trace generated by the crash, which contains references to +* The stack trace generated by the crash, which contains references to the Flutter CLI's own code and contains no references to your application code. * A client ID: a constant and unique number generated for @@ -25,13 +25,13 @@ A crash report might contain the following information: It also helps us verify if a fix works as intended after you upgrade to the next version of Flutter. -Google handles all data reported by this tool in accordance with the +Google handles all data reported by this tool in accordance with the [Google Privacy Policy][]. -You may review the recently reported data in the +You may review the recently reported data in the `.dart-tool/dart-flutter-telemetry.log` file. On macOS or Linux, this log is located in the home directory (`~/`). -On Windows, this log is located in the Roaming AppData directory (`%APPDATA%`). +On Windows, this log is located in the Roaming AppData directory (`%APPDATA%`). ## Disabling analytics reporting @@ -43,9 +43,9 @@ $ flutter --disable-analytics ``` If you opt out of analytics, Flutter sends an opt-out event. -This Flutter installation neither sends nor stores any further information. +This Flutter installation neither sends nor stores any further information. -To opt into analytics, run the following command: +To opt into analytics, run the following command: ```console $ flutter --enable-analytics diff --git a/src/content/reference/security-false-positives.md b/src/content/reference/security-false-positives.md index 02c11de6350..58dbdaefc80 100644 --- a/src/content/reference/security-false-positives.md +++ b/src/content/reference/security-false-positives.md @@ -72,7 +72,7 @@ simply because pure Dart code is a managed language where things like buffer overruns don't exist. In principle, you can create vulnerable code when using Dart FFI, -but normal use of Dart FFI would not be prone to these issues either, +but normal use of Dart FFI would not be prone to these issues either, assuming it's used with C code that itself uses stack canary values appropriately. @@ -148,7 +148,7 @@ HLS is Apple's streaming format, which defines the type of encryption that must be used for DRM; this isn't a vulnerability, as DRM doesn't protect the user's machine or data -but instead merely provides obfuscation +but instead merely provides obfuscation to limit the user's ability to fully use their software and hardware. ### Apps can read and write to external storage diff --git a/src/content/reference/user-surveys.md b/src/content/reference/user-surveys.md index 135744a71c7..41dc320aec3 100644 --- a/src/content/reference/user-surveys.md +++ b/src/content/reference/user-surveys.md @@ -7,25 +7,25 @@ showBreadcrumbs: false ## Why do I see a survey announcement? -If you have not opted-out of Flutter's -[analytics and crash reporting](/reference/crash-reporting), -you may receive a survey announcement in your IDE. +If you have not opted-out of Flutter's +[analytics and crash reporting](/reference/crash-reporting), +you may receive a survey announcement in your IDE. We run two types of surveys: 1. **Each quarter.** We give all active Flutter and Dart users the option to take this survey. 2. **Ad-hoc.** We design and deploy experimental surveys - when we want to learn more about specific topics. + when we want to learn more about specific topics. If your telemetry data meets the survey criteria, - you might see a survey announcement. + you might see a survey announcement. ## How will my responses be used? -We use the responses you submit via survey to improve -Flutter and Dart. We store this information independent +We use the responses you submit via survey to improve +Flutter and Dart. We store this information independent of the information sent to Google via analytics. To see how we used prior surveys to improve Flutter and Dart, -check out our blogs on [Medium][]. +check out our blogs on [Medium][]. ## How can I disable it? @@ -34,7 +34,7 @@ To mute survey announcements, you might do one of the following: 1. Click the button on the message. 2. Opt-out of analytics and crash reporting per the steps given in - [Disabling + [Disabling analytics reporting](/reference/crash-reporting#disabling-analytics-reporting). [Medium]: {{site.flutter-medium}}/search?q=survey diff --git a/src/content/release/archive-whats-new.md b/src/content/release/archive-whats-new.md index e8c45867fec..77596a20036 100644 --- a/src/content/release/archive-whats-new.md +++ b/src/content/release/archive-whats-new.md @@ -93,7 +93,7 @@ This website release includes several important updates! Also, check out the [Video & web app support in Flutter][ad-bp] blog post. -* We have new docs for using Flutter with Android, specifically, +* We have new docs for using Flutter with Android, specifically, [Launching a Jetpack Compose activity from your Flutter application][jc] and [Calling JetPack APIs][jetpack-api]. @@ -111,7 +111,7 @@ This website release includes several important updates! [Swift Package Manager for app authors][app-authors]. * The [Deep linking validator tool][deep-linking-tool], part of DevTools, - now works for both iOS and Android. + now works for both iOS and Android. * Also, don't forget to check out the [breaking changes][bc-3.27] page for this release. That's also where you'll find useful @@ -221,7 +221,7 @@ and the [Flutter 3.22 technical blog post][3.22-tech]. You might also check out the [Dart 3.4 release][] blog post. In particular, Dart now provides a "baked in" language macro, -`JsonCodable`, for serializing and deserializing JSON data. +`JsonCodable`, for serializing and deserializing JSON data. A future (and unspecified) Dart release will allow you to create your own macros. To learn more, check out [dart.dev/go/macros][]. @@ -540,7 +540,7 @@ You might also check out [Introducing Dart 3][]. * Added guide on [fonts and typography][]. * Added guide on restoring state on [Android][] and [iOS][] Flutter apps. * Added a section about [sharing iOS and macOS plugin implementations][]. -* Added a guide on adapting the Material +* Added a guide on adapting the Material [top app bar and navigation bar][], and [bottom navigation bar][] widgets to the current platform as a start of UI component platform adaptation guidelines. diff --git a/src/content/release/breaking-changes/1-22-deprecations.md b/src/content/release/breaking-changes/1-22-deprecations.md index 55c95ab2fd8..a8d2a6941d3 100644 --- a/src/content/release/breaking-changes/1-22-deprecations.md +++ b/src/content/release/breaking-changes/1-22-deprecations.md @@ -1,7 +1,7 @@ --- title: Deprecated API removed after v1.22 description: > - After reaching end of life, + After reaching end of life, the following deprecated APIs were removed from Flutter. --- @@ -288,7 +288,7 @@ Relevant PRs: * Deprecated in [#15303][] * Removed in [#72532][] - + [`showDialog`]: {{site.api}}/flutter/material/showDialog.html [showDialog should take a builder rather than a child]: {{site.repo.flutter}}/issues/14341 [#15303]: {{site.repo.flutter}}/pull/15303 @@ -304,7 +304,7 @@ The `resizeToAvoidBottomPadding` parameter of `Scaffold` was deprecated in v1.1. The `resizeToAvoidBottomInset` parameter should be used instead. **Migration guide** - + Code before migration: ```dart @@ -500,7 +500,7 @@ The `RenderView.scheduleInitialFrame` method was deprecated and removed in order to prevent splash screens from being taken down too early, resulting in a black screen. This would happen when `WidgetsFlutterBinding.ensureInitialized` was called. -Instead, replace calls to this method with `RenderView.prepareInitialFrame`, +Instead, replace calls to this method with `RenderView.prepareInitialFrame`, followed by `RenderView.owner.requestVisualUpdate`. **Migration guide** diff --git a/src/content/release/breaking-changes/3-10-deprecations.md b/src/content/release/breaking-changes/3-10-deprecations.md index f160e9cb028..4daab7f9cac 100644 --- a/src/content/release/breaking-changes/3-10-deprecations.md +++ b/src/content/release/breaking-changes/3-10-deprecations.md @@ -41,7 +41,7 @@ Code before migration: ```dart var themeData = ThemeData( - fixTextFieldOutlineLabel: true, + fixTextFieldOutlineLabel: true, ); ``` @@ -136,7 +136,7 @@ in v2.6. The replacements are the `ColorScheme.primaryContainer` and `ColorScheme.secondaryContainer`, respectively. These changes were made to align with the updated Material Design specification -for `ColorScheme`. The updates to `ColorScheme` are covered more extensively in +for `ColorScheme`. The updates to `ColorScheme` are covered more extensively in the [ColorScheme for Material 3][] design document. **Migration guide** diff --git a/src/content/release/breaking-changes/3-13-deprecations.md b/src/content/release/breaking-changes/3-13-deprecations.md index a62173f60cd..3521ad3798e 100644 --- a/src/content/release/breaking-changes/3-13-deprecations.md +++ b/src/content/release/breaking-changes/3-13-deprecations.md @@ -42,7 +42,7 @@ same result as setting the original property `useDeleteButtonTooltip` to false. When `deleteButtonTooltipMessage` is unset, the `MaterialLocalizations.deleteButtonTooltip` is used by default. -The [Deprecate `useDeleteButtonTooltip` for Chips][] design document +The [Deprecate `useDeleteButtonTooltip` for Chips][] design document covers this update to chips and tooltips in greater depth. To learn more, check out the [chips and tooltips migration guide][]. diff --git a/src/content/release/breaking-changes/3-16-deprecations.md b/src/content/release/breaking-changes/3-16-deprecations.md index fecd23913bb..1852e53587a 100644 --- a/src/content/release/breaking-changes/3-16-deprecations.md +++ b/src/content/release/breaking-changes/3-16-deprecations.md @@ -128,7 +128,7 @@ Code before migration: ```dart ThemeData( // ... - selectedRowColor: Colors.pink, // Would have no effect. + selectedRowColor: Colors.pink, // Would have no effect. ); ``` @@ -137,7 +137,7 @@ Code after migration: ```dart ThemeData( // ... - // Remove uses. + // Remove uses. ); ``` diff --git a/src/content/release/breaking-changes/3-19-deprecations.md b/src/content/release/breaking-changes/3-19-deprecations.md index 19225f9a0ae..79912d4f4fd 100644 --- a/src/content/release/breaking-changes/3-19-deprecations.md +++ b/src/content/release/breaking-changes/3-19-deprecations.md @@ -715,12 +715,12 @@ Relevant PRs: Supported by Flutter Fix: no -As of Flutter 3.0 platform views require api 23 or higher. In Flutter 3.19 we now throw UnsupportedOperationException -when using platform views on android devices running api level 22 and below. +As of Flutter 3.0 platform views require api 23 or higher. In Flutter 3.19 we now throw UnsupportedOperationException +when using platform views on android devices running api level 22 and below. **Migration guide** -Set minimum api level to 23 (or higher) or check the android api level before displaying a platform view. +Set minimum api level to 23 (or higher) or check the android api level before displaying a platform view. --- diff --git a/src/content/release/breaking-changes/3-7-deprecations.md b/src/content/release/breaking-changes/3-7-deprecations.md index 68f8195d215..c6dbc8bc4b6 100644 --- a/src/content/release/breaking-changes/3-7-deprecations.md +++ b/src/content/release/breaking-changes/3-7-deprecations.md @@ -57,7 +57,7 @@ Code before migration: ```dart var myRecognizer = GestureRecognizer( - kind: PointerDeviceKind.mouse, + kind: PointerDeviceKind.mouse, ); ``` diff --git a/src/content/release/breaking-changes/add-showAutocorrectionPromptRect.md b/src/content/release/breaking-changes/add-showAutocorrectionPromptRect.md index 7da8b9c8431..9bb8c268cfa 100644 --- a/src/content/release/breaking-changes/add-showAutocorrectionPromptRect.md +++ b/src/content/release/breaking-changes/add-showAutocorrectionPromptRect.md @@ -1,7 +1,7 @@ --- -title: showAutocorrectionPromptRect method added to TextInputClient +title: showAutocorrectionPromptRect method added to TextInputClient description: > - A new method, void showAutocorrectionPromptRect(int start, int end), + A new method, void showAutocorrectionPromptRect(int start, int end), was added to the TextInputClient interface --- @@ -30,7 +30,7 @@ highlighted candidate changes. If your application doesn't implement or subclass `TextInputClient`, no migration is needed. If your application doesn't target iOS, -or the class that implemented the `textInputClient` interface doesn't +or the class that implemented the `textInputClient` interface doesn't support autocorrect, you only need to add an empty implementation for the new method: @@ -42,13 +42,13 @@ class CustomTextInputClient implements TextInputClient { Otherwise, if your app targets iOS and supports autocorrect on iOS, we recommend that you add a sensible implementation of -`void showAutocorrectionPromptRect(int start, int end)` -to your `TextInputClient` subclass. +`void showAutocorrectionPromptRect(int start, int end)` +to your `TextInputClient` subclass. Code after migration: ```dart -// Assume your `TextInputClient` is a `State` subclass, and it has a variable +// Assume your `TextInputClient` is a `State` subclass, and it has a variable // `_currentPromptRectRange` that controls the autocorrection highlight. class CustomTextInputClient extends State<...> implements TextInputClient { @override diff --git a/src/content/release/breaking-changes/android-14-nonlinear-text-scaling-migration.md b/src/content/release/breaking-changes/android-14-nonlinear-text-scaling-migration.md index 7befe71510e..e6e656efb1f 100644 --- a/src/content/release/breaking-changes/android-14-nonlinear-text-scaling-migration.md +++ b/src/content/release/breaking-changes/android-14-nonlinear-text-scaling-migration.md @@ -38,7 +38,7 @@ check out the To opt-out of nonlinear text scaling on Android 14 until you migrate your app, add a modified `MediaQuery` at the top of your app's widget tree: -```dart +```dart runApp( Builder(builder: (context) { final mediaQueryData = MediaQuery.of(context); @@ -71,7 +71,7 @@ Relevant PRs: * [Implementing TextScaler for nonlinear text scaling][] -See also: +See also: * [Deprecate `textScaleFactor` in favor of `TextScaler`][] diff --git a/src/content/release/breaking-changes/android-java-gradle-migration-guide.md b/src/content/release/breaking-changes/android-java-gradle-migration-guide.md index 68de3c05a2e..a9476b8d18e 100644 --- a/src/content/release/breaking-changes/android-java-gradle-migration-guide.md +++ b/src/content/release/breaking-changes/android-java-gradle-migration-guide.md @@ -61,7 +61,7 @@ Upgrade the Gradle version in Android Studio Flamingo as follows: 1. In Android Studio, open the `android` folder. - This should bring up the following dialog: + This should bring up the following dialog: ![Dialog prompting you to upgrade Gradle](/assets/images/docs/releaseguide/android-studio-flamingo-upgrade-alert.png){:width="50%"} @@ -88,21 +88,21 @@ Do the following from the top of your Flutter project. ``` ## You didn't update Android Studio and still have a Java error -The error appears similar to `Unsupported class file major version 65`. +The error appears similar to `Unsupported class file major version 65`. This is an indication that your Java version is newer than the version of gradle you are running can handle. There is a non obvious set of dependencies -surrounding AGP, Java, and Gradle. +surrounding AGP, Java, and Gradle. -### Solution 1: Android Studio -The easiest way to resolve this issue is to use Android Studio AGP upgrade assistant. -To use select your top-level `build.gradle` file in Android Studio then select -Tools -> AGP Upgrade Assistant. +### Solution 1: Android Studio +The easiest way to resolve this issue is to use Android Studio AGP upgrade assistant. +To use select your top-level `build.gradle` file in Android Studio then select +Tools -> AGP Upgrade Assistant. ### Solution 2: Command line -Run `flutter analyze --suggestions` to see if your AGP, Java, and Gradle versions are compatible. +Run `flutter analyze --suggestions` to see if your AGP, Java, and Gradle versions are compatible. If Gradle needs to be updated you can update it with `./gradlew wrapper --gradle-version=SOMEGRADLEVERSION` where SOMEGRADLEVERSION is the version (you can use a newer version) -suggested by `flutter analyze`. +suggested by `flutter analyze`. To find the Java version being used run `flutter doctor`. On a mac, you can find the Java versions that the OS knows about with `/usr/libexec/java_home -V`. diff --git a/src/content/release/breaking-changes/android-predictive-back.md b/src/content/release/breaking-changes/android-predictive-back.md index 884363bb4f6..bad12548756 100644 --- a/src/content/release/breaking-changes/android-predictive-back.md +++ b/src/content/release/breaking-changes/android-predictive-back.md @@ -306,7 +306,7 @@ if (_route.popDisposition == RoutePopDisposition.doNotPop) { ### Migrating a back confirmation dialog `WillPopScope` was sometimes used to show a confirmation -dialog when a back gesture was received. +dialog when a back gesture was received. This can still be done with `PopScope` in a similar pattern. Code before migration: diff --git a/src/content/release/breaking-changes/android-setIsRunningInRobolectricTest-removed.md b/src/content/release/breaking-changes/android-setIsRunningInRobolectricTest-removed.md index 3312d6bac6b..2e33015a609 100644 --- a/src/content/release/breaking-changes/android-setIsRunningInRobolectricTest-removed.md +++ b/src/content/release/breaking-changes/android-setIsRunningInRobolectricTest-removed.md @@ -1,7 +1,7 @@ --- title: FlutterMain.setIsRunningInRobolectricTest on Android removed description: > - The test-only FlutterMain.setIsRunningInRobolectricTest API on the + The test-only FlutterMain.setIsRunningInRobolectricTest API on the Android engine is consolidated into the FlutterInjector. --- @@ -52,7 +52,7 @@ the `libflutter.so` library. This [engine commit][] removed the `FlutterMain.setIsRunningInRobolectricTest()` testing function; -and the following [commit][] added a +and the following [commit][] added a `FlutterInjector` class to assist testing. [PR 20473][] further refactored `FlutterLoader` and `FlutterJNI` to allow for additional mocking and testing. diff --git a/src/content/release/breaking-changes/animation-sheet-builder-display.md b/src/content/release/breaking-changes/animation-sheet-builder-display.md index e12c16ee67c..b0943c5f11b 100644 --- a/src/content/release/breaking-changes/animation-sheet-builder-display.md +++ b/src/content/release/breaking-changes/animation-sheet-builder-display.md @@ -74,7 +74,7 @@ tester.binding.setSurfaceSize(animationSheet.sheetSize(600)); ``` * The frames per row should be the result of the two - numbers divided, rounded down. For example, + numbers divided, rounded down. For example, 600 / 80 = 7 (rounded down), therefore ```dart diff --git a/src/content/release/breaking-changes/bottom-navigation-title-to-label.md b/src/content/release/breaking-changes/bottom-navigation-title-to-label.md index ebc768659ee..3b233cb47ee 100644 --- a/src/content/release/breaking-changes/bottom-navigation-title-to-label.md +++ b/src/content/release/breaking-changes/bottom-navigation-title-to-label.md @@ -2,7 +2,7 @@ title: Bottom Navigation Title To Label description: > Deprecated BottomNavigationBarItem's title (a Widget) in - favor of label (a String). + favor of label (a String). --- {% render "docs/breaking-changes.md" %} @@ -18,7 +18,7 @@ or no longer exists when referenced in code. in favor of `label`. This change was necessary to improve the user experience of `BottomNavigationBar`s when the text scale factor is increased. Items in a `BottomNavigationBar` -now show tooltips on long press. Accomplishing this +now show tooltips on long press. Accomplishing this requires a `String` parameter on `BottomNavigationBarItem`s. ## Description of change @@ -26,8 +26,8 @@ requires a `String` parameter on `BottomNavigationBarItem`s. The `BottomNavigationBarItem` class has a `title` parameter, which is a `Widget`. This made it impossible for the `BottomNavigationBar` to show `Tooltip` widgets, -a change that was necessary to improve the accessibility experience. -Now, instead of building the `BottomNavigationBarItem.title` +a change that was necessary to improve the accessibility experience. +Now, instead of building the `BottomNavigationBarItem.title` widget, the BottomNavigationBar wraps the `BottomNavigationBarItem.label` in a Text widget and builds that. diff --git a/src/content/release/breaking-changes/component-theme-normalization-updates.md b/src/content/release/breaking-changes/component-theme-normalization-updates.md index 8d2356c1f36..bbdfdf9a720 100644 --- a/src/content/release/breaking-changes/component-theme-normalization-updates.md +++ b/src/content/release/breaking-changes/component-theme-normalization-updates.md @@ -125,4 +125,3 @@ Relevant PRs: [`InputDecorationTheme`]: {{site.api}}/flutter/material/InputDecorationTheme-class.html [Normalize InputDecorationTheme]: {{site.repo.flutter}}/pull/168981 [Apply normalization to TimePickerThemeData.inputDecorationTheme]: {{site.repo.flutter}}/pull/171584 - diff --git a/src/content/release/breaking-changes/component-theme-normalization.md b/src/content/release/breaking-changes/component-theme-normalization.md index 6b4aa5aa26d..6be45e83a04 100644 --- a/src/content/release/breaking-changes/component-theme-normalization.md +++ b/src/content/release/breaking-changes/component-theme-normalization.md @@ -9,7 +9,7 @@ description: >- ## Summary -`CardTheme`, `DialogTheme` and `TabBarTheme` were refactored to +`CardTheme`, `DialogTheme` and `TabBarTheme` were refactored to conform to Flutter's conventions for component themes. `CardThemeData`, `DialogThemeData`, and `TabBarThemeData` were added to define overrides for the defaults of the component visual properties. diff --git a/src/content/release/breaking-changes/context-menus.md b/src/content/release/breaking-changes/context-menus.md index 444fe10839b..a82dff1503d 100644 --- a/src/content/release/breaking-changes/context-menus.md +++ b/src/content/release/breaking-changes/context-menus.md @@ -1,7 +1,7 @@ --- title: A new way to customize context menus description: > - Several hard-coded parameters for customizing context menus have + Several hard-coded parameters for customizing context menus have now been replaced by a generic widget builder. --- diff --git a/src/content/release/breaking-changes/default-desktop-scrollbars.md b/src/content/release/breaking-changes/default-desktop-scrollbars.md index 2057600536b..2ba77a56096 100644 --- a/src/content/release/breaking-changes/default-desktop-scrollbars.md +++ b/src/content/release/breaking-changes/default-desktop-scrollbars.md @@ -32,7 +32,7 @@ what is built around the scrollable. Furthermore, `ScrollBehavior` subclasses `MaterialScrollBehavior` and `CupertinoScrollBehavior` have been made public, allowing developers to extend and build upon the other existing `ScrollBehavior`s in the framework. These -subclasses were previously private. +subclasses were previously private. ## Description of change @@ -94,7 +94,7 @@ control and configure this feature. - Extend `ScrollBehavior`, `MaterialScrollBehavior`, or `CupertinoScrollBehavior` to modify the default behavior. - + - With your own `ScrollBehavior`, you can apply it app-wide by setting `MaterialApp.scrollBehavior` or `CupertinoApp.scrollBehavior`. - Or, if you wish to only apply it to specific widgets, add a diff --git a/src/content/release/breaking-changes/default-scroll-behavior-drag.md b/src/content/release/breaking-changes/default-scroll-behavior-drag.md index fd7429c26b9..ab032247d93 100644 --- a/src/content/release/breaking-changes/default-scroll-behavior-drag.md +++ b/src/content/release/breaking-changes/default-scroll-behavior-drag.md @@ -34,13 +34,13 @@ control and configure this feature. - Extend `ScrollBehavior`, `MaterialScrollBehavior`, or `CupertinoScrollBehavior` to modify the default behavior, overriding `ScrollBehavior.dragDevices`. - + - With your own `ScrollBehavior`, you can apply it app-wide by setting `MaterialApp.scrollBehavior` or `CupertinoApp.scrollBehavior`. - Or, if you wish to only apply it to specific widgets, add a `ScrollConfiguration` above the widget in question with your custom `ScrollBehavior`. - + Your scrollable widgets then inherit and reflect this behavior. - Instead of creating your own `ScrollBehavior`, another option for changing @@ -77,7 +77,7 @@ Code after migration: class MyCustomScrollBehavior extends MaterialScrollBehavior { // Override behavior methods and getters like dragDevices @override - Set get dragDevices => { + Set get dragDevices => { PointerDeviceKind.touch, PointerDeviceKind.mouse, // etc. @@ -111,7 +111,7 @@ Code after migration: class MyCustomScrollBehavior extends MaterialScrollBehavior { // Override behavior methods and getters like dragDevices @override - Set get dragDevices => { + Set get dragDevices => { PointerDeviceKind.touch, PointerDeviceKind.mouse, // etc. diff --git a/src/content/release/breaking-changes/deprecate-overlay-portal-targets-root.md b/src/content/release/breaking-changes/deprecate-overlay-portal-targets-root.md index 924b541de70..f0202fba101 100644 --- a/src/content/release/breaking-changes/deprecate-overlay-portal-targets-root.md +++ b/src/content/release/breaking-changes/deprecate-overlay-portal-targets-root.md @@ -8,7 +8,7 @@ description: >- ## Summary -The `OverlayPortal.targetsRootOverlay` property was deprecated and +The `OverlayPortal.targetsRootOverlay` property was deprecated and replaced with `overlayLocation`. ## Context diff --git a/src/content/release/breaking-changes/deprecate-textscalefactor.md b/src/content/release/breaking-changes/deprecate-textscalefactor.md index 09b5694cc40..dc8e5aac9cf 100644 --- a/src/content/release/breaking-changes/deprecate-textscalefactor.md +++ b/src/content/release/breaking-changes/deprecate-textscalefactor.md @@ -9,26 +9,26 @@ description: >- ## Summary -In preparation for adopting the [Android 14 nonlinear font scaling][] feature, -all occurrences of `textScaleFactor` in the Flutter framework have been -deprecated and replaced by `TextScaler`. +In preparation for adopting the [Android 14 nonlinear font scaling][] feature, +all occurrences of `textScaleFactor` in the Flutter framework have been +deprecated and replaced by `TextScaler`. ## Context Many platforms allow users to scale textual contents up or down globally in -system preferences. In the past, the scaling strategy was captured as a single -`double` value named `textScaleFactor`, as text scaling was proportional: -`scaledFontSize = textScaleFactor x unScaledFontSize`. For example, when -`textScaleFactor` is 2.0 and the developer-specified font size is 14.0, the +system preferences. In the past, the scaling strategy was captured as a single +`double` value named `textScaleFactor`, as text scaling was proportional: +`scaledFontSize = textScaleFactor x unScaledFontSize`. For example, when +`textScaleFactor` is 2.0 and the developer-specified font size is 14.0, the actual font size is 2.0 x 14.0 = 28.0. With the introduction of [Android 14 nonlinear font scaling][], larger text gets scaled at a lesser rate as compared to smaller text, to prevent excessive scaling -of text that is already large. The `textScaleFactor` scalar value used by +of text that is already large. The `textScaleFactor` scalar value used by "proportional" scaling is not enough to represent this new scaling strategy. -The [Replaces `textScaleFactor` with `TextScaler`][] pull request introduced a +The [Replaces `textScaleFactor` with `TextScaler`][] pull request introduced a new class `TextScaler` to replace `textScaleFactor` in preparation for this new -feature. Nonlinear text scaling is introduced in a different pull request. +feature. Nonlinear text scaling is introduced in a different pull request. ## Description of change @@ -36,18 +36,18 @@ Introducing a new interface `TextScaler`, which represents a text scaling strategy. ```dart -abstract class TextScaler { +abstract class TextScaler { double scale(double fontSize); - double get textScaleFactor; // Deprecated. + double get textScaleFactor; // Deprecated. } ``` Use the `scale` method to scale font sizes instead of `textScaleFactor`. -The `textScaleFactor` getter provides an estimated `textScaleFactor` value, it +The `textScaleFactor` getter provides an estimated `textScaleFactor` value, it is for backward compatibility purposes and is already marked as deprecated, and will be removed in a future version of Flutter. -The new class has replaced +The new class has replaced `double textScaleFactor` (`double textScaleFactor` -> `TextScaler textScaler`), in the following APIs: @@ -107,32 +107,32 @@ deprecated symbols listed in the previous tables. Before: -```dart -abstract class _MyCustomPaintDelegate { - void paint(PaintingContext context, Offset offset, double textScaleFactor) { +```dart +abstract class _MyCustomPaintDelegate { + void paint(PaintingContext context, Offset offset, double textScaleFactor) { } } ``` After: -```dart -abstract class _MyCustomPaintDelegate { - void paint(PaintingContext context, Offset offset, TextScaler textScaler) { +```dart +abstract class _MyCustomPaintDelegate { + void paint(PaintingContext context, Offset offset, TextScaler textScaler) { } } ``` ### Migrating code that consumes `textScaleFactor` -If you're not currently using `textScaleFactor` directly, but rather passing it -to a different API that receives a `textScaleFactor`, and the receiver API has +If you're not currently using `textScaleFactor` directly, but rather passing it +to a different API that receives a `textScaleFactor`, and the receiver API has already been migrated, then it's relatively straightforward: Before: -```dart -RichText( +```dart +RichText( textScaleFactor: MediaQuery.textScaleFactorOf(context), ... ) @@ -140,44 +140,44 @@ RichText( After: -```dart -RichText( +```dart +RichText( textScaler: MediaQuery.textScalerOf(context), ... ) ``` -If the API that provides `textScaleFactor` hasn't been migrated, consider +If the API that provides `textScaleFactor` hasn't been migrated, consider waiting for the migrated version. -If you wish to compute the scaled font size yourself, use `TextScaler.scale` +If you wish to compute the scaled font size yourself, use `TextScaler.scale` instead of the `*` binary operator: Before: -```dart +```dart final scaledFontSize = textStyle.fontSize * MediaQuery.textScaleFactorOf(context); ``` After: -```dart +```dart final scaledFontSize = MediaQuery.textScalerOf(context).scale(textStyle.fontSize); ``` -If you are using `textScaleFactor` to scale dimensions that are not font sizes, -there are no generic rules for migrating the code to nonlinear scaling, and it +If you are using `textScaleFactor` to scale dimensions that are not font sizes, +there are no generic rules for migrating the code to nonlinear scaling, and it might require the UI to be implemented differently. Reusing the `MyTooltipBox`example: -```dart -MyTooltipBox( +```dart +MyTooltipBox( size: chatBoxSize * textScaleFactor, child: RichText(..., style: TextStyle(fontSize: 20)), ) ``` -You could choose to use the "effective" text scale factor by applying the +You could choose to use the "effective" text scale factor by applying the `TextScaler` on the font size 20: `chatBoxSize * textScaler.scale(20) / 20`, or redesign the UI and let the widget assume its own intrinsic size. @@ -189,7 +189,7 @@ the `MediaQuery` like so: Before: ```dart -MediaQuery( +MediaQuery( data: MediaQuery.of(context).copyWith(textScaleFactor: 2.0), child: child, ) @@ -198,18 +198,18 @@ MediaQuery( After: ```dart -MediaQuery( +MediaQuery( data: MediaQuery.of(context).copyWith(textScaler: _myCustomTextScaler), child: child, ) ``` However, it's rarely needed to create a custom `TextScaler` subclass. -`MediaQuery.withNoTextScaling` (which creates a widget that disables text scaling -altogether for its child subtree), and `MediaQuery.withClampedTextScaling` (which -creates a widget that restricts the scaled font size to within the range -`[minScaleFactor * fontSize, maxScaleFactor * fontSize]`), are convenience methods -that cover common cases where the text scaling strategy needs to be overridden. +`MediaQuery.withNoTextScaling` (which creates a widget that disables text scaling +altogether for its child subtree), and `MediaQuery.withClampedTextScaling` (which +creates a widget that restricts the scaled font size to within the range +`[minScaleFactor * fontSize, maxScaleFactor * fontSize]`), are convenience methods +that cover common cases where the text scaling strategy needs to be overridden. #### Examples @@ -217,7 +217,7 @@ that cover common cases where the text scaling strategy needs to be overridden. Before: -```dart +```dart MediaQuery( data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), child: IconTheme( @@ -229,7 +229,7 @@ MediaQuery( After: -```dart +```dart MediaQuery.withNoTextScaling( child: IconTheme( data: ... @@ -242,7 +242,7 @@ MediaQuery.withNoTextScaling( Before: -```dart +```dart final mediaQueryData = MediaQuery.of(context); MediaQuery( data: mediaQueryData.copyWith(textScaleFactor: math.min(mediaQueryData.textScaleFactor, _kMaxTitleTextScaleFactor), @@ -252,7 +252,7 @@ MediaQuery( After: -```dart +```dart MediaQuery.withClampedTextScaling( maxScaleFactor: _kMaxTitleTextScaleFactor, child: title, @@ -261,11 +261,11 @@ MediaQuery.withClampedTextScaling( **Disabling Nonlinear Text Scaling** -If you want to temporarily opt-out of nonlinear text scaling on Android 14 until -your app is fully migrated, put a modified `MediaQuery` at the top of your app's +If you want to temporarily opt-out of nonlinear text scaling on Android 14 until +your app is fully migrated, put a modified `MediaQuery` at the top of your app's widget tree: -```dart +```dart runApp( Builder(builder: (context) { final mediaQueryData = MediaQuery.of(context); @@ -292,7 +292,7 @@ API documentation: * [`MediaQuery.textScalerOf`][] * [`MediaQuery.maybeTextScalerOf`][] * [`MediaQuery.withNoTextScaling`][] -* [`MediaQuery.withClampedTextScaling`][] +* [`MediaQuery.withClampedTextScaling`][] Relevant issues: @@ -312,4 +312,3 @@ Relevant PRs: [New font scaling system (Issue 116231)]: {{site.repo.flutter}}/issues/116231 [Replaces `textScaleFactor` with `TextScaler`]: {{site.repo.flutter}}/pull/128522 - diff --git a/src/content/release/breaking-changes/describe-enum.md b/src/content/release/breaking-changes/describe-enum.md index 8da24ea0a93..0025dba766a 100644 --- a/src/content/release/breaking-changes/describe-enum.md +++ b/src/content/release/breaking-changes/describe-enum.md @@ -12,7 +12,7 @@ of `describeEnum(Enum.something)` should use `Enum.something.name` instead. The class `EnumProperty` was modified to -extend `` instead of ``. +extend `` instead of ``. Existing uses of `EnumProperty` should use `DiagnosticsProperty` instead. diff --git a/src/content/release/breaking-changes/dispose.md b/src/content/release/breaking-changes/dispose.md index bc63ae5ba66..ec60e1170f4 100644 --- a/src/content/release/breaking-changes/dispose.md +++ b/src/content/release/breaking-changes/dispose.md @@ -9,14 +9,14 @@ description: > ## Summary Missing calls to 'dispose()' are added for some disposable objects. -For example, ContextMenuController did not dispose OverlayEntry, +For example, ContextMenuController did not dispose OverlayEntry, and EditableTextState did not dispose TextSelectionOverlay. If some other code also invokes 'dispose()' for the object, and the object is protected from double disposal, the second 'dispose()' fails with the following error message: -`Once you have called dispose() on a , it can no longer be used.` +`Once you have called dispose() on a , it can no longer be used.` ## Background diff --git a/src/content/release/breaking-changes/editable-text-focus-attachment.md b/src/content/release/breaking-changes/editable-text-focus-attachment.md index 9053a821535..3807d52b945 100644 --- a/src/content/release/breaking-changes/editable-text-focus-attachment.md +++ b/src/content/release/breaking-changes/editable-text-focus-attachment.md @@ -64,7 +64,7 @@ field or a selectable text field like so: Then please read on and consider following the migration steps to avoid breakages. -If you're not sure whether a codebase needs migration, +If you're not sure whether a codebase needs migration, search for `is EditableText`, `as EditableText`, `is EditableTextState`, and `as EditableTextState` and verify if any of the search results are doing a typecheck or typecast on a `FocusNode.context`. diff --git a/src/content/release/breaking-changes/enterText-trailing-caret.md b/src/content/release/breaking-changes/enterText-trailing-caret.md index 3cba7a8354b..f45a5406fcb 100644 --- a/src/content/release/breaking-changes/enterText-trailing-caret.md +++ b/src/content/release/breaking-changes/enterText-trailing-caret.md @@ -44,22 +44,22 @@ your tests to adopt the `enterText` change.** Common test failures this change may introduce includes: -- Golden test failures: +- Golden test failures: The caret appears at the end of the text, as opposed to before the text prior to the change. - + - Different `TextEditingValue.selection` after calling `enterText`: - The text field's `TextEditingValue` now has a collapsed - selection with a non-negative offset, as opposed to + The text field's `TextEditingValue` now has a collapsed + selection with a non-negative offset, as opposed to `TextSelection.collapsed(offset: -1)` prior to the change. - For instance, you may see + For instance, you may see `expect(controller.value.selection.baseOffset, -1);` failing after `enterText` calls. If your tests have to rely on setting the selection to invalid, -the previous behavior can be achieved using`updateEditingValue`: +the previous behavior can be achieved using`updateEditingValue`: ### `TestTextInput.enterText` diff --git a/src/content/release/breaking-changes/expansion-tile-controller.md b/src/content/release/breaking-changes/expansion-tile-controller.md index 83e9d71b537..f06397d8bc6 100644 --- a/src/content/release/breaking-changes/expansion-tile-controller.md +++ b/src/content/release/breaking-changes/expansion-tile-controller.md @@ -28,7 +28,7 @@ Code before migration: ```dart class _MyWidgetState extends State { final ExpansionTileController controller = ExpansionTileController(); - + @override Widget build(BuildContext context) { return ExpansionTile( @@ -43,13 +43,13 @@ Code after migration: ```dart class _MyWidgetState extends State { final ExpansibleController controller = ExpansibleController(); - + @override void dispose() { controller.dispose(); super.dispose(); } - + @override Widget build(BuildContext context) { return ExpansionTile( diff --git a/src/content/release/breaking-changes/flutter-driver-migration.md b/src/content/release/breaking-changes/flutter-driver-migration.md index 7af64b5a9e1..4556ffeae1c 100644 --- a/src/content/release/breaking-changes/flutter-driver-migration.md +++ b/src/content/release/breaking-changes/flutter-driver-migration.md @@ -65,7 +65,7 @@ dev_dependencies: ``` Next, in your project, create a new directory -`integration_test/`, create your tests files there +`integration_test/`, create your tests files there with the format: `_test.dart`. ## Test migration @@ -75,7 +75,7 @@ This section contains different examples on how to migrate existing ### Example: Verifying a widget is displayed -When the app starts the screen on the right displays +When the app starts the screen on the right displays a text asking the user to select one of the plants on the list. This test verifies that the text is displayed. @@ -219,7 +219,7 @@ use the `driver.scroll` method. You must provide the widget to perform the scrolling action, as well as a duration for the scroll. -You also have to provide the total offset for the scrolling action. +You also have to provide the total offset for the scrolling action. ```dart diff --git a/src/content/release/breaking-changes/flutter-gradle-plugin-apply.md b/src/content/release/breaking-changes/flutter-gradle-plugin-apply.md index 3c6097386b6..2161673c321 100644 --- a/src/content/release/breaking-changes/flutter-gradle-plugin-apply.md +++ b/src/content/release/breaking-changes/flutter-gradle-plugin-apply.md @@ -37,9 +37,9 @@ buildscripts. First, find the values of the Android Gradle Plugin (AGP) and Kotlin that the project currently uses. Unless they have been moved, -they are likely defined in the buildscript block of the +they are likely defined in the buildscript block of the `/android/build.gradle` file. -As an example, consider the `build.gradle` file from +As an example, consider the `build.gradle` file from a new Flutter app created before this change: ```groovy @@ -76,8 +76,8 @@ tasks.register("clean", Delete) { } ``` -The AGP version is the number that comes at the end of the line -`classpath 'com.android.tools.build:gradle:7.3.0'`, so `7.3.0` +The AGP version is the number that comes at the end of the line +`classpath 'com.android.tools.build:gradle:7.3.0'`, so `7.3.0` in this case. Similarly, the Kotlin version comes at the end of the line `ext.kotlin_version = '1.7.10'`, in this case `1.7.10`. @@ -195,12 +195,12 @@ plugins { id "dev.flutter.flutter-gradle-plugin" } ``` -`"dev.flutter.flutter-gradle-plugin"` is the project Flutter -Gradle Plugin, which is a different string than the value applied -in settings.gradle(.kts) (`"dev.flutter.flutter-plugin-loader"`). +`"dev.flutter.flutter-gradle-plugin"` is the project Flutter +Gradle Plugin, which is a different string than the value applied +in settings.gradle(.kts) (`"dev.flutter.flutter-plugin-loader"`). Finally, if your `dependencies` block contains a dependency -on `"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"`, +on `"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"`, then remove that dependency. ```groovy diff diff --git a/src/content/release/breaking-changes/flutter-memory-allocations.md b/src/content/release/breaking-changes/flutter-memory-allocations.md index 6f8b8918625..3c4af68ba89 100644 --- a/src/content/release/breaking-changes/flutter-memory-allocations.md +++ b/src/content/release/breaking-changes/flutter-memory-allocations.md @@ -8,7 +8,7 @@ description: >- ## Summary -Disposables in pure Dart projects can't use `MemoryAllocations` in Flutter. +Disposables in pure Dart projects can't use `MemoryAllocations` in Flutter. So, to be leak-trackable they need a Dart-only class. `MemoryAllocations` in Flutter is renamed to make the name available to a non-Flutter, Dart project. @@ -17,7 +17,7 @@ available to a non-Flutter, Dart project. Before: -```dart +```dart if (kFlutterMemoryAllocationsEnabled) { MemoryAllocations.instance.dispatchObjectCreated( library: 'package:flutter/gestures.dart', @@ -29,7 +29,7 @@ if (kFlutterMemoryAllocationsEnabled) { After: -```dart +```dart if (kFlutterMemoryAllocationsEnabled) { FlutterMemoryAllocations.instance.dispatchObjectCreated( library: 'package:flutter/gestures.dart', diff --git a/src/content/release/breaking-changes/form-semantics.md b/src/content/release/breaking-changes/form-semantics.md index b15b0dde66c..f0348e7a5c9 100644 --- a/src/content/release/breaking-changes/form-semantics.md +++ b/src/content/release/breaking-changes/form-semantics.md @@ -28,7 +28,7 @@ can provide better information to accessibility services. ## Description of change -The core change is the integration of a semantics widget +The core change is the integration of a semantics widget into the Form widget's build method. ## Migration guide diff --git a/src/content/release/breaking-changes/ignoringsemantics-migration.md b/src/content/release/breaking-changes/ignoringsemantics-migration.md index 8bc36e97f36..f57eaeb2a7e 100644 --- a/src/content/release/breaking-changes/ignoringsemantics-migration.md +++ b/src/content/release/breaking-changes/ignoringsemantics-migration.md @@ -17,7 +17,7 @@ was introduced as a workaround to preserve the semantics tree when using `IgnorePointer`s. The `IgnorePointer` behavior has changed in that it no longer drops -the entire semantics subtree but merely blocks semantics actions in the +the entire semantics subtree but merely blocks semantics actions in the subtree. The `ignoringSemantics` workaround is no longer needed and is deprecated. diff --git a/src/content/release/breaking-changes/insert-content-text-input-client.md b/src/content/release/breaking-changes/insert-content-text-input-client.md index cd7e97c5655..5c8a0107eb8 100644 --- a/src/content/release/breaking-changes/insert-content-text-input-client.md +++ b/src/content/release/breaking-changes/insert-content-text-input-client.md @@ -2,7 +2,7 @@ title: Insert content text input client description: > Add a new method to the TextInputClient interface to allow - Android virtual keyboards to insert rich content into Flutter TextFields. + Android virtual keyboards to insert rich content into Flutter TextFields. --- {% render "docs/breaking-changes.md" %} diff --git a/src/content/release/breaking-changes/ios-flutterviewcontroller-splashscreenview-nullable.md b/src/content/release/breaking-changes/ios-flutterviewcontroller-splashscreenview-nullable.md index b1af6396bd1..4dcca91d3d7 100644 --- a/src/content/release/breaking-changes/ios-flutterviewcontroller-splashscreenview-nullable.md +++ b/src/content/release/breaking-changes/ios-flutterviewcontroller-splashscreenview-nullable.md @@ -28,7 +28,7 @@ New declaration of `splashScreenView`: Prior to this change, on iOS the `splashScreenView` property returned `nil` when no splash screen view was set, and setting the property to `nil` removed the splash screen view. -However, the `splashScreenView` API was incorrectly marked `nonnull`. +However, the `splashScreenView` API was incorrectly marked `nonnull`. This property is most often used when transitioning to Flutter views in iOS add-to-app scenarios. diff --git a/src/content/release/breaking-changes/kotlin-version.md b/src/content/release/breaking-changes/kotlin-version.md index cb3bf538da6..de172857052 100644 --- a/src/content/release/breaking-changes/kotlin-version.md +++ b/src/content/release/breaking-changes/kotlin-version.md @@ -1,5 +1,5 @@ --- -title: Required Kotlin version +title: Required Kotlin version description: > Flutter apps built for the Android platform now require Kotlin 1.5.31 or greater. diff --git a/src/content/release/breaking-changes/macos-windows-merged-threads.md b/src/content/release/breaking-changes/macos-windows-merged-threads.md index ce5692884fd..c1f4f736eac 100644 --- a/src/content/release/breaking-changes/macos-windows-merged-threads.md +++ b/src/content/release/breaking-changes/macos-windows-merged-threads.md @@ -29,7 +29,7 @@ Flutter 3.29. Merged threads should not affect your app. -If you suspect merged threads has regressed your app, please reach out on +If you suspect merged threads has regressed your app, please reach out on [Issue 150525][]. ## Timeline @@ -51,4 +51,3 @@ Relevant PRs: [Issue 150525]: {{site.repo.flutter}}/issues/150525 [PR 166536]: {{site.repo.flutter}}/pull/166536 [PR 167472]: {{site.repo.flutter}}/pull/167472 - diff --git a/src/content/release/breaking-changes/material-3-default.md b/src/content/release/breaking-changes/material-3-default.md index 9bd577428a0..4df5482850d 100644 --- a/src/content/release/breaking-changes/material-3-default.md +++ b/src/content/release/breaking-changes/material-3-default.md @@ -96,4 +96,3 @@ Relevant PRs: [Add support for M3 motion]: {{site.repo.flutter}}/issues/129942 [Change the default for `ThemeData.useMaterial3` to true]: {{site.repo.flutter}}/pull/129724 [Updated `ThemeData.useMaterial3` API doc, default is true]: {{site.repo.flutter}}/pull/130764 - diff --git a/src/content/release/breaking-changes/material-design-3-token-update.md b/src/content/release/breaking-changes/material-design-3-token-update.md index 2a0849fb7d0..5c8af3c6521 100644 --- a/src/content/release/breaking-changes/material-design-3-token-update.md +++ b/src/content/release/breaking-changes/material-design-3-token-update.md @@ -75,7 +75,7 @@ final chip = ChipTheme( ), ), child: ActionChip( - label: const Text('action chip'), + label: const Text('action chip'), onPressed: () {} ) ); diff --git a/src/content/release/breaking-changes/material-theme-system-updates.md b/src/content/release/breaking-changes/material-theme-system-updates.md index 99f545709a1..c8eb428954f 100644 --- a/src/content/release/breaking-changes/material-theme-system-updates.md +++ b/src/content/release/breaking-changes/material-theme-system-updates.md @@ -2,7 +2,7 @@ title: Material Theme System Updates description: >- `CardTheme`, `DialogTheme`, and `TabBarTheme` have been normalized to follow - Flutter's convention for component themes in the Material library. In + Flutter's convention for component themes in the Material library. In `ThemeData`, the type of these properties have also changed accordingly. --- @@ -10,8 +10,8 @@ description: >- ## Summary -`CardTheme`, `DialogTheme` and `TabBarTheme` were refactored to -conform to Flutter's conventions for component themes. `CardThemeData`, +`CardTheme`, `DialogTheme` and `TabBarTheme` were refactored to +conform to Flutter's conventions for component themes. `CardThemeData`, `DialogThemeData` and `TabBarThemeData` were added to define overrides for the defaults of the component visual properties. @@ -20,7 +20,7 @@ to `Object?` to accept both `CardTheme` and `CardThemeData`, in order to have a smooth transition for the breaking changes. The same approach was used for `dialogTheme` and `tabBarTheme`. -To complete the transition and fully conform to the `ThemeData` convention, the +To complete the transition and fully conform to the `ThemeData` convention, the type of `ThemeData.cardTheme` has been changed to `CardThemeData?`; the type of `ThemeData.dialogTheme` has been changed to `DialogThemeData?`; and the type of `ThemeData.tabBarTheme` has been changed to `TabBarThemeData?`. @@ -30,7 +30,7 @@ type of `ThemeData.cardTheme` has been changed to `CardThemeData?`; the type of Previously, the type of `ThemeData.cardTheme` was `Object?` to accept both `CardTheme` and `CardThemeData`. Now that the type has been changed to `CardThemeData?`, a migration is required if `ThemeData.cardTheme` is used. -Similarly, the types of `ThemeData.dialogTheme` and `ThemeData.tabBarTheme` +Similarly, the types of `ThemeData.dialogTheme` and `ThemeData.tabBarTheme` should be migrated to `DialogThemeData` and `TabBarThemeData`, respectively. Code before migration: diff --git a/src/content/release/breaking-changes/menus-text-style.md b/src/content/release/breaking-changes/menus-text-style.md index 7d42e118af6..572b256b898 100644 --- a/src/content/release/breaking-changes/menus-text-style.md +++ b/src/content/release/breaking-changes/menus-text-style.md @@ -15,7 +15,7 @@ to match the Material 3 specification. ## Context The default text style for `MenuItemButton` (a widget used -in a `MenuBar`, and in a menu created with `MenuAnchor`), +in a `MenuBar`, and in a menu created with `MenuAnchor`), and `DropdownMenuEntry` (in the `DropdownMenu`) is updated to match the Material 3 specification. @@ -25,7 +25,7 @@ to match the Material 3 specification. ## Description of change The default text style for `MenuItemButton` (a widget used -in a `MenuBar`, and in a menu created with `MenuAnchor`), +in a `MenuBar`, and in a menu created with `MenuAnchor`), and `DropdownMenuEntry` (in the `DropdownMenu`) is updated from `TextTheme.bodyLarge` to `TextTheme.labelLarge` for Material 3. @@ -35,7 +35,7 @@ The default text style for the `DropdownMenu`s `TextField` is updated from ## Migration guide A `MenuItemButton` for Material 3 uses -`TextTheme.labelLarge` as the default text style. +`TextTheme.labelLarge` as the default text style. To use the previous default text style, set the `TextTheme.bodyLarge` text style in the `MenuItemButton.style` or `MenuButtonThemeData.style` properties. @@ -76,10 +76,10 @@ menuButtonTheme: MenuButtonThemeData( ), ``` -A `DropdownMenu`'s `TextField` for Material 3 -uses `TextTheme.bodyLarge` as the default text style. -To use the previous default text style, -set the `TextTheme.labelLarge` text style in +A `DropdownMenu`'s `TextField` for Material 3 +uses `TextTheme.bodyLarge` as the default text style. +To use the previous default text style, +set the `TextTheme.labelLarge` text style in the `DropdownMenu.textStyle` or `DropdownMenuThemeData.textStyle` properties. Code before migration: @@ -130,8 +130,8 @@ dropdownMenuTheme: DropdownMenuThemeData( ), ``` -A `DropdownMenu`'s `DropdownMenuEntry` for Material 3 -uses `TextTheme.labelLarge` as the default text style. +A `DropdownMenu`'s `DropdownMenuEntry` for Material 3 +uses `TextTheme.labelLarge` as the default text style. To use the previous default text style, set the `TextTheme.bodyLarge` text style in the `DropdownMenuEntry.style` or `MenuButtonThemeData.style` properties. diff --git a/src/content/release/breaking-changes/mouse-tracker-moved-to-rendering.md b/src/content/release/breaking-changes/mouse-tracker-moved-to-rendering.md index fa059cf9747..47fc5f507d1 100644 --- a/src/content/release/breaking-changes/mouse-tracker-moved-to-rendering.md +++ b/src/content/release/breaking-changes/mouse-tracker-moved-to-rendering.md @@ -73,11 +73,11 @@ Relevant PR: * [Move mouse_tracking.dart to rendering][] [Move annotations to a separate tree]: {{site.repo.flutter}}/issues/49568 -[Move mouse_tracking.dart to rendering]: {{site.repo.flutter}}/pull/52781 +[Move mouse_tracking.dart to rendering]: {{site.repo.flutter}}/pull/52781 [Transform mouse events to the local coordinate system]: {{site.repo.flutter}}/issues/33675 [`MouseDetectorAnnotationFinder`]: {{site.api}}/flutter/gestures/MouseDetectorAnnotationFinder.html [`MouseTracker`]: {{site.api}}/flutter/gestures/MouseTracker-class.html -[`MouseTrackerAnnotation`]: {{site.api}}/flutter/gestures/MouseTrackerAnnotation-class.html -[`PointerEnterEventListener`]: {{site.api}}/flutter/gestures/PointerEnterEventListener.html -[`PointerExitEventListener`]: {{site.api}}/flutter/gestures/PointerExitEventListener.html -[`PointerHoverEventListener`]: {{site.api}}/flutter/gestures/PointerHoverEventListener.html +[`MouseTrackerAnnotation`]: {{site.api}}/flutter/gestures/MouseTrackerAnnotation-class.html +[`PointerEnterEventListener`]: {{site.api}}/flutter/gestures/PointerEnterEventListener.html +[`PointerExitEventListener`]: {{site.api}}/flutter/gestures/PointerExitEventListener.html +[`PointerHoverEventListener`]: {{site.api}}/flutter/gestures/PointerHoverEventListener.html diff --git a/src/content/release/breaking-changes/mouse-tracker-no-longer-attaches-annotations.md b/src/content/release/breaking-changes/mouse-tracker-no-longer-attaches-annotations.md index a7480be9960..d9ff410a65d 100644 --- a/src/content/release/breaking-changes/mouse-tracker-no-longer-attaches-annotations.md +++ b/src/content/release/breaking-changes/mouse-tracker-no-longer-attaches-annotations.md @@ -2,7 +2,7 @@ title: MouseTracker no longer attaches annotations description: > MouseTracker no longer relies on annotation attachment to - perform the mounted-exit check; therefore, + perform the mounted-exit check; therefore, all three related methods are removed. --- diff --git a/src/content/release/breaking-changes/multi-touch-scrolling.md b/src/content/release/breaking-changes/multi-touch-scrolling.md index e01a4e96ccc..82b5c019b68 100644 --- a/src/content/release/breaking-changes/multi-touch-scrolling.md +++ b/src/content/release/breaking-changes/multi-touch-scrolling.md @@ -9,7 +9,7 @@ description: > ## Summary -`ScrollBehavior`s now allow or disallow scrolling speeds to be affected by the +`ScrollBehavior`s now allow or disallow scrolling speeds to be affected by the number of pointers on the screen. `ScrollBehavior.multitouchDragStrategy`, by default, prevents multiple pointers interacting wih the scrollable at the same time from affecting the speed of scrolling. diff --git a/src/content/release/breaking-changes/new-color-scheme-roles.md b/src/content/release/breaking-changes/new-color-scheme-roles.md index 3239b4237a3..05e5ad3cd85 100644 --- a/src/content/release/breaking-changes/new-color-scheme-roles.md +++ b/src/content/release/breaking-changes/new-color-scheme-roles.md @@ -22,7 +22,7 @@ adapting to the Material Design 3 guidelines. ## Background -The tone-based surface colors include: +The tone-based surface colors include: - `surfaceBright` - `surfaceDim` @@ -34,7 +34,7 @@ The tone-based surface colors include: These changes help eliminate the use of widgets' `surfaceTintColor`, and replaces the old opacity-based model that applied a tinted overlay -on top of surfaces based on their elevation. +on top of surfaces based on their elevation. The default `surfaceTintColor` for all widgets is now `null` and their default background color is now diff --git a/src/content/release/breaking-changes/plugin-api-migration.md b/src/content/release/breaking-changes/plugin-api-migration.md index 0756c588c78..11e158d52ed 100644 --- a/src/content/release/breaking-changes/plugin-api-migration.md +++ b/src/content/release/breaking-changes/plugin-api-migration.md @@ -253,10 +253,10 @@ but aren't required. ```dart import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; - + void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - + testWidgets('Can get battery level', (tester) async { final Battery battery = Battery(); final int batteryLevel = await battery.batteryLevel; diff --git a/src/content/release/breaking-changes/popscope-with-result.md b/src/content/release/breaking-changes/popscope-with-result.md index c3e3ab0bb25..2b7d2801b4f 100644 --- a/src/content/release/breaking-changes/popscope-with-result.md +++ b/src/content/release/breaking-changes/popscope-with-result.md @@ -26,7 +26,7 @@ the new method `onPopInvokedWithResult` can access the type-safe result. ## Description of change Added a generic type (``) to the `PopScope` class and -a new method `onPopInvokedWithResult`. +a new method `onPopInvokedWithResult`. The `onPopInvoked` property was deprecated in favor of `onPopInvokedWithResult`. Also added a new method `onPopInvokedWithResult` diff --git a/src/content/release/breaking-changes/rendereditable-layout-before-hit-test.md b/src/content/release/breaking-changes/rendereditable-layout-before-hit-test.md index 9e14347c2ae..1b421a6b93c 100644 --- a/src/content/release/breaking-changes/rendereditable-layout-before-hit-test.md +++ b/src/content/release/breaking-changes/rendereditable-layout-before-hit-test.md @@ -22,7 +22,7 @@ Failed assertion: line 123 pos 45: '!debugNeedsLayout': is not true. To support gesture recognizers in selectable text, the `RenderEditable` requires the layout information for its text spans to determine which text span receives the -pointer event. (Before this change, `RenderEditable` objects +pointer event. (Before this change, `RenderEditable` objects didn't take their text into account when evaluating hit tests.) To implement this, layout was made a prerequisite for performing hit testing on a `RenderEditable` object. diff --git a/src/content/release/breaking-changes/route-navigator-refactoring.md b/src/content/release/breaking-changes/route-navigator-refactoring.md index 3f35be237f8..9f8bc328ef2 100644 --- a/src/content/release/breaking-changes/route-navigator-refactoring.md +++ b/src/content/release/breaking-changes/route-navigator-refactoring.md @@ -30,7 +30,7 @@ defined, and the user could achieve the same result by calling `Navigator.canPop()`. Since the API for `Navigator.canPop()` was better defined, we simplified `Navigator.pop()` to not return a boolean value. - + On the other hand, the navigator requires the ability to manually rearrange entries in the overlay to allow the user to change the route history in the new API. diff --git a/src/content/release/breaking-changes/scaffold-messenger.md b/src/content/release/breaking-changes/scaffold-messenger.md index 6544f13a679..7c9f138b970 100644 --- a/src/content/release/breaking-changes/scaffold-messenger.md +++ b/src/content/release/breaking-changes/scaffold-messenger.md @@ -157,7 +157,7 @@ scaffoldMessengerKey.currentState.showSnackBar(mySnackBar); scaffoldMessengerKey.currentState.hideCurrentSnackBar(mySnackBar); scaffoldMessengerKey.currentState.removeCurrentSnackBar(mySnackBar); -// The root ScaffoldMessenger can also be accessed by providing a key to +// The root ScaffoldMessenger can also be accessed by providing a key to // MaterialApp.scaffoldMessengerKey. This way, the ScaffoldMessengerState can be directly accessed // without first obtaining it from a BuildContext via ScaffoldMessenger.of. From the key, use // the GlobalKey.currentState getter. diff --git a/src/content/release/breaking-changes/scribble-text-input-client.md b/src/content/release/breaking-changes/scribble-text-input-client.md index 6d5d2fb3104..e82b3baf8b7 100644 --- a/src/content/release/breaking-changes/scribble-text-input-client.md +++ b/src/content/release/breaking-changes/scribble-text-input-client.md @@ -61,12 +61,12 @@ class MyCustomTextInputClient implements TextInputClient { void showToolbar() { ... } - + @override void insertTextPlaceholder(Size size) { ... } - + @override void removeTextPlaceholder() { ... diff --git a/src/content/release/breaking-changes/snackbar-with-action-behavior-update.md b/src/content/release/breaking-changes/snackbar-with-action-behavior-update.md index 3733a33cdba..35f13ae7bc5 100644 --- a/src/content/release/breaking-changes/snackbar-with-action-behavior-update.md +++ b/src/content/release/breaking-changes/snackbar-with-action-behavior-update.md @@ -10,7 +10,7 @@ description: >- ## Summary The default behavior of a [`SnackBar`][] with an action has changed. Previously, a -`SnackBar` with an action would not auto-dismiss if talkback was enabled. +`SnackBar` with an action would not auto-dismiss if talkback was enabled. Now, all `SnackBar`s with an action default to a non-dismissible state until the user interacts with the action button. @@ -26,14 +26,14 @@ until they are acknowledged. This change aligns with the Material 3 design specifications for `SnackBar`s: * Old behavior: A `SnackBar` with an action button would auto-dismiss after a duration unless talkback was enabled. -* New behavior: A `SnackBar` with an action button won't auto-dismiss; +* New behavior: A `SnackBar` with an action button won't auto-dismiss; it remains on screen until dismissed by the user. -To override this behavior, an optional `persist` property has -been added to `SnackBar`. When `persist` is true, the `SnackBar` won't auto-dismiss -and remains on screen until manually dismissed by the user. When false, the -`SnackBar` auto-dismisses after its standard duration, regardless of the -presence of an action. When null, the `SnackBar` follows the default +To override this behavior, an optional `persist` property has +been added to `SnackBar`. When `persist` is true, the `SnackBar` won't auto-dismiss +and remains on screen until manually dismissed by the user. When false, the +`SnackBar` auto-dismisses after its standard duration, regardless of the +presence of an action. When null, the `SnackBar` follows the default behavior, which won't auto-dismiss if an action is present. ## Migration guide diff --git a/src/content/release/breaking-changes/tooltip-semantics-order.md b/src/content/release/breaking-changes/tooltip-semantics-order.md index f84978fe3a2..81e1d3b8893 100644 --- a/src/content/release/breaking-changes/tooltip-semantics-order.md +++ b/src/content/release/breaking-changes/tooltip-semantics-order.md @@ -1,5 +1,5 @@ --- -title: Accessibility traversal order of tooltip changed +title: Accessibility traversal order of tooltip changed description: >- The Tooltip widget's message now immediately follows the Tooltip widget's child during accessibility traversal. @@ -15,7 +15,7 @@ visited immediately after `Tooltip.child`. ## Background The `Tooltip` widget usually wraps an interactive UI component such as a button, -and shows a help message when long pressed. +and shows a help message when long pressed. When the message is visible, assistive technologies should announce it after the button. diff --git a/src/content/release/breaking-changes/trackpad-gestures.md b/src/content/release/breaking-changes/trackpad-gestures.md index 9d0b9a61e3d..7fc758e589d 100644 --- a/src/content/release/breaking-changes/trackpad-gestures.md +++ b/src/content/release/breaking-changes/trackpad-gestures.md @@ -2,7 +2,7 @@ title: Trackpad gestures can trigger GestureRecognizer description: > Trackpad gestures on most platforms now send `PointerPanZoom` sequences and - can trigger pan, drag, and scale `GestureRecognizer` callbacks. + can trigger pan, drag, and scale `GestureRecognizer` callbacks. --- {% render "docs/breaking-changes.md" %} diff --git a/src/content/release/breaking-changes/visibility-maintainfocusability.md b/src/content/release/breaking-changes/visibility-maintainfocusability.md index 583b3cf8470..668ff2d3f74 100644 --- a/src/content/release/breaking-changes/visibility-maintainfocusability.md +++ b/src/content/release/breaking-changes/visibility-maintainfocusability.md @@ -20,11 +20,11 @@ A new flag, `maintainFocusability`, must be set to true with `maintainState` for a hidden widget to remain focusable. ## Migration guide -If your app has a `Visibility` widget that does not set `maintainState` to true, +If your app has a `Visibility` widget that does not set `maintainState` to true, then no changes are required. If your app has a `Visibility` widget that sets `maintainState` to true -and you relied on the previous default behavior +and you relied on the previous default behavior that allowed you to focus your hidden widget, you will need to set `maintainFocusability` to true. diff --git a/src/content/release/breaking-changes/wide-gamut-cupertino-dynamic-color.md b/src/content/release/breaking-changes/wide-gamut-cupertino-dynamic-color.md index 85558c90bca..c75f2b000e8 100644 --- a/src/content/release/breaking-changes/wide-gamut-cupertino-dynamic-color.md +++ b/src/content/release/breaking-changes/wide-gamut-cupertino-dynamic-color.md @@ -15,8 +15,8 @@ added in [Flutter 3.27][Migration guide for wide gamut Color]. ## Context -The `Color` class was updated to support wide gamut color spaces, but some -corresponding deprecations were not initially applied to +The `Color` class was updated to support wide gamut color spaces, but some +corresponding deprecations were not initially applied to `CupertinoDynamicColor` due to its implementation rather than due to the extension of `Color`. diff --git a/src/content/release/breaking-changes/window-singleton.md b/src/content/release/breaking-changes/window-singleton.md index 37240d77e23..d557f40e7d2 100644 --- a/src/content/release/breaking-changes/window-singleton.md +++ b/src/content/release/breaking-changes/window-singleton.md @@ -1,7 +1,7 @@ --- title: The window singleton is deprecated description: > - In preparation for supporting multiple views and + In preparation for supporting multiple views and multiple windows the window singleton has been deprecated. --- diff --git a/src/content/release/breaking-changes/zone-errors.md b/src/content/release/breaking-changes/zone-errors.md index 3387c223a05..5bceb641e71 100644 --- a/src/content/release/breaking-changes/zone-errors.md +++ b/src/content/release/breaking-changes/zone-errors.md @@ -116,7 +116,7 @@ void main() { } ``` -In code that needs to use `myKey`, +In code that needs to use `myKey`, it can be obtained indirectly using `Zone.current['myKey'].value`. When such a solution does not work diff --git a/src/content/release/compatibility-policy.md b/src/content/release/compatibility-policy.md index 0713bd5ca21..53b65d164a9 100644 --- a/src/content/release/compatibility-policy.md +++ b/src/content/release/compatibility-policy.md @@ -16,8 +16,8 @@ those tests to (a) determine if the change is sufficiently valuable, and (b) provide fixes for the code so that the tests continue to pass. If you would like to provide tests as part of this program, please -submit a PR to the [flutter/tests repository][]. -The [README][flutter-tests-readme] on that repository describes +submit a PR to the [flutter/tests repository][]. +The [README][flutter-tests-readme] on that repository describes the process in detail. [flutter/tests repository]: {{site.github}}/flutter/tests diff --git a/src/content/release/release-notes/changelogs/changelog-1.12.13.md b/src/content/release/release-notes/changelogs/changelog-1.12.13.md index 1571c050e10..356bef7bcb8 100644 --- a/src/content/release/release-notes/changelogs/changelog-1.12.13.md +++ b/src/content/release/release-notes/changelogs/changelog-1.12.13.md @@ -3829,4 +3829,3 @@ From Sun Aug 19 17:37:00 2019 -0700 to Mon Nov 25 12:05:00 2019 -0800 [2296](https://github.com/flutter/plugins/pull/2296) [shared_preferences] Add missing DartDoc (cla: yes) [2297](https://github.com/flutter/plugins/pull/2297) [share] README update (cla: yes) - diff --git a/src/content/release/release-notes/changelogs/changelog-1.2.1.md b/src/content/release/release-notes/changelogs/changelog-1.2.1.md index ec4e6a97aa6..368db2a2077 100644 --- a/src/content/release/release-notes/changelogs/changelog-1.2.1.md +++ b/src/content/release/release-notes/changelogs/changelog-1.2.1.md @@ -1,5 +1,5 @@ --- -title: Change log for Flutter 1.2.2 +title: Change log for Flutter 1.2.2 shortTitle: 1.2.2 change log description: Change log for Flutter 1.2.2 containing a list of all PRs merged for this release. skipTemplateRendering: true diff --git a/src/content/release/release-notes/changelogs/changelog-1.5.4.md b/src/content/release/release-notes/changelogs/changelog-1.5.4.md index 353fdbc3791..6b9b957e052 100644 --- a/src/content/release/release-notes/changelogs/changelog-1.5.4.md +++ b/src/content/release/release-notes/changelogs/changelog-1.5.4.md @@ -1,5 +1,5 @@ --- -title: Change log for Flutter 1.5.4 +title: Change log for Flutter 1.5.4 shortTitle: 1.5.4 change log description: Change log for Flutter 1.5.4 containing a list of all PRs merged for this release. skipTemplateRendering: true diff --git a/src/content/release/release-notes/changelogs/changelog-1.7.8.md b/src/content/release/release-notes/changelogs/changelog-1.7.8.md index 8b6d5d74e77..15ed248f30a 100644 --- a/src/content/release/release-notes/changelogs/changelog-1.7.8.md +++ b/src/content/release/release-notes/changelogs/changelog-1.7.8.md @@ -1,5 +1,5 @@ --- -title: Change log for Flutter 1.7.8 +title: Change log for Flutter 1.7.8 shortTitle: 1.7.8 change log description: Change log for Flutter 1.7.8 containing a list of all PRs merged for this release. skipTemplateRendering: true diff --git a/src/content/release/release-notes/changelogs/changelog-1.9.1.md b/src/content/release/release-notes/changelogs/changelog-1.9.1.md index 7a58d7d27c4..3463509841f 100644 --- a/src/content/release/release-notes/changelogs/changelog-1.9.1.md +++ b/src/content/release/release-notes/changelogs/changelog-1.9.1.md @@ -1,5 +1,5 @@ --- -title: Change log for Flutter 1.9.1 +title: Change log for Flutter 1.9.1 shortTitle: 1.9.1 change log description: Change log for Flutter 1.9.1 containing a list of all PRs merged for this release. skipTemplateRendering: true @@ -1894,4 +1894,3 @@ From Fri Jun 21 22:31:55 2019 -0400 to Sun Aug 18 12:22:00 2019 -0700 [11072](https://github.com/flutter/engine/pull/11072) Roll src/third_party/dart beee442625..79e6c74337 (8 commits) (cla: yes) [11075](https://github.com/flutter/engine/pull/11075) [dynamic_thread_merging] Resubmit only on the frame where the merge (cla: yes) - diff --git a/src/content/release/release-notes/release-notes-0.0.21-1.0.0.md b/src/content/release/release-notes/release-notes-0.0.21-1.0.0.md index 508206187f9..862899248e6 100644 --- a/src/content/release/release-notes/release-notes-0.0.21-1.0.0.md +++ b/src/content/release/release-notes/release-notes-0.0.21-1.0.0.md @@ -14,17 +14,17 @@ _This page is a dump of the old Changelog page from the Flutter wiki up until * [video_player image distortion problem after last flutter update 0.11.3](https://github.com/flutter/flutter/issues/24402) * [Green, flickering bar over camera preview](https://github.com/flutter/flutter/issues/24289) * [Image rendering issues on Adreno 3xx devices after upgrade from 0.9.4 to 0.10.2](https://github.com/flutter/flutter/issues/24517) -* Engine rolls to fix +* Engine rolls to fix * [Prepend [NSLocale currentLocale] for first locale on iOS to ensure countryCode exists. Allow language-only locales.](https://github.com/flutter/engine/issues/6995) * [Changes to unblock Fuchsia roll](https://github.com/flutter/engine/issues/6949) * Various fixes to tooling for documentation, documentation, and documentation accompanying the templates. ## Changes since v0.10.2 * [flutter/engine#6883](https://github.com/flutter/engine/pull/6883) - FlutterViewController will no longer load your app's splash screen by default. The implementation of that has been moved to a new method `loadDefaultSplashScreenView`. -* [#23755](https://github.com/flutter/flutter/pull/23755) Removed direct dependency of flutter_test on `package:test`. Flutter now requires test version 1.5.1 and mockito version 4.0.0. +* [#23755](https://github.com/flutter/flutter/pull/23755) Removed direct dependency of flutter_test on `package:test`. Flutter now requires test version 1.5.1 and mockito version 4.0.0. ### Breaking change: - This requires adding an explicit dependency to your pubspec.yaml: + This requires adding an explicit dependency to your pubspec.yaml: ```yaml dev_dependencies: test: ^1.5.1 @@ -72,7 +72,7 @@ _This page is a dump of the old Changelog page from the Flutter wiki up until * [flutter/engine#6393](https://github.com/flutter/engine/pull/6393) adds nullability annotations to Android MethodChannel/MethodCall. ### v0.9.6 -* [#21251](https://github.com/flutter/flutter/pull/21251) adds CupertinoDatePicker, an iOS-style picker control that supports a date mode and a date + time mode. +* [#21251](https://github.com/flutter/flutter/pull/21251) adds CupertinoDatePicker, an iOS-style picker control that supports a date mode and a date + time mode. ## Changes in v0.9.4 (since v0.8.2 ) - beta 9 @@ -364,4 +364,3 @@ To follow our investigation, see [Dart issue 32936](https://github.com/dart-lang * [#4487](https://github.com/flutter/engine/pull/4487) replaces all uses of the `RequestPermissionResult` callback concept in `io.flutter.plugin.common.PluginRegistry` with `RequestPermissionsResult`, adding a missing `s` to align with the corresponding Android SDK concept. The old API has been deprecated and will be made unavailable in a later release. There will be a grace period of at least four weeks between the release that introduces the deprecation and the release that makes the old API unavailable. - diff --git a/src/content/release/release-notes/release-notes-1.12.13.md b/src/content/release/release-notes/release-notes-1.12.13.md index 628d70fdf8d..39b0eb19cbc 100644 --- a/src/content/release/release-notes/release-notes-1.12.13.md +++ b/src/content/release/release-notes/release-notes-1.12.13.md @@ -330,7 +330,7 @@ and [CupertinoDatePicker]({{site.api}}/flutter/cupertino/CupertinoDatePicker-cla ## Android -In this release, we've merged a list of changes to support Android 10, including a new activity zoom transition. +In this release, we've merged a list of changes to support Android 10, including a new activity zoom transition. [37526]({{site.repo.flutter}}/pull/37526) catch errors during gradle update @@ -399,7 +399,7 @@ In this release, we've merged a list of changes to support Android 10, including ## Add to App feature -We've made a significant upgrade to Add-to-App, the feature that allows you to integrate a Flutter module into your Android or iOS app. Can't wait to try it? Check out the [Add-to-App documentation](/add-to-app). +We've made a significant upgrade to Add-to-App, the feature that allows you to integrate a Flutter module into your Android or iOS app. Can't wait to try it? Check out the [Add-to-App documentation](/add-to-app). [41666]({{site.repo.flutter}}/pull/41666) Generate projects using the new Android embedding @@ -958,7 +958,7 @@ Material continues to a focus for the Flutter team. In this release, we refreshed all Material widgets with dark mode support. Also, we added support for extending the height of the Scaffold's body behind -the app bar, which was contributed by a community member! +the app bar, which was contributed by a community member! [36998]({{site.repo.flutter}}/pull/36998) Added properties in DropdownButtonFormField to match DropdownButton @@ -1129,7 +1129,7 @@ In Text and Accessibility, we have several enhancements in ButtonBar and AlertDi For animation, we released the [TweenAnimationBuilder]({{site.api}}/flutter/widgets/TweenAnimationBuilder-class.html) for building custom implicit animations. For more information, -check out this [TweenAnimationBuilder video](https://www.youtube.com/watch?reload=9&v=6KiPEqzJIKQ) on Youtube. +check out this [TweenAnimationBuilder video](https://www.youtube.com/watch?reload=9&v=6KiPEqzJIKQ) on Youtube. [38317]({{site.repo.flutter}}/pull/38317) TweenAnimationBuilder for building custom animations without managing an AnimationController @@ -1475,7 +1475,7 @@ enabling it in the dev channel. ## Framework -We've fixed many bugs in this release to improve the quality and stability of our framework. +We've fixed many bugs in this release to improve the quality and stability of our framework. [38643]({{site.repo.flutter}}/pull/38643) PlatformViewLink handles focus @@ -1612,7 +1612,7 @@ We've fixed many bugs in this release to improve the quality and stability of ou ## Engine -In this update, the core engine continues to see many improvements, including a fix that solves the long-requested scrolling performance issue on iPhoneX/Xs. +In this update, the core engine continues to see many improvements, including a fix that solves the long-requested scrolling performance issue on iPhoneX/Xs. [9386]({{site.repo.engine}}/pull/9386) [glfw] Send the glfw key data to the framework. @@ -2468,7 +2468,7 @@ General bug fixes in the e2e plugin. #### Plugin: Google Maps Flutter -We have made several improvements in the Google Maps plugin including adding support for displaying the traffic layer. +We have made several improvements in the Google Maps plugin including adding support for displaying the traffic layer. [1702]({{site.github}}/flutter/plugins/pull/1702) [google_maps_flutter]Marker drag event diff --git a/src/content/release/release-notes/release-notes-1.17.0.md b/src/content/release/release-notes/release-notes-1.17.0.md index 72dd97cbe4b..3696397aa34 100644 --- a/src/content/release/release-notes/release-notes-1.17.0.md +++ b/src/content/release/release-notes/release-notes-1.17.0.md @@ -7,7 +7,7 @@ skipTemplateRendering: true ## Merged pull requests by label -### Merged PRs by labels for `flutter/flutter` +### Merged PRs by labels for `flutter/flutter` #### tool - 435 pull request(s) @@ -5602,7 +5602,7 @@ skipTemplateRendering: true [57037](https://github.com/flutter/flutter/pull/57037) Making DropdownButtonFormField to re-render if parent widget changes (cla: yes, f: material design, found in release: 1.17, found in release: 1.18, framework, severe: regression, waiting for tree to go green) -### Merged PRs by labels for `flutter/engine` +### Merged PRs by labels for `flutter/engine` #### platform-android - 62 pull request(s) @@ -6106,7 +6106,7 @@ skipTemplateRendering: true -### Merged PRs by labels for `flutter/plugins` +### Merged PRs by labels for `flutter/plugins` #### submit queue - 3 pull request(s) @@ -12492,5 +12492,3 @@ There were 214 pull requests. [2627](https://github.com/flutter/plugins/pull/2627) [google_maps_flutter] Rename 'Page' in the example app to avoid type conflict with the Flutter Framework. (cla: yes) [2628](https://github.com/flutter/plugins/pull/2628) [path_provider] Fix misaligned enum values in StorageDirectoryMapperTest (cla: yes) - - diff --git a/src/content/release/release-notes/release-notes-1.2.1.md b/src/content/release/release-notes/release-notes-1.2.1.md index 159274ed2a9..0787c9138f5 100644 --- a/src/content/release/release-notes/release-notes-1.2.1.md +++ b/src/content/release/release-notes/release-notes-1.2.1.md @@ -45,7 +45,7 @@ In addition to the iOS Cupertino theme support, this release continues to enhanc [#25339](https://github.com/flutter/flutter/pull/25339) [Material] Theme-able TextStyles for AlertDialog -To integrate more fully with desktop form-factors like Android tablets and ChromeOS as well as desktop web and desktop OS support, this release builds more support for keyboard and mouse as first class input devices: +To integrate more fully with desktop form-factors like Android tablets and ChromeOS as well as desktop web and desktop OS support, this release builds more support for keyboard and mouse as first class input devices: [#7758](https://github.com/flutter/engine/pull/7758) Recommended implementation of combining characters implementation @@ -185,19 +185,19 @@ Flutter assets for iOS applications are now found in Frameworks/App.framework/fl ### [#27697](https://github.com/flutter/flutter/pull/27697) Cupertino TextField Cursor Fix -CupertinoTextField's cursorColor default now matches the app's theme. If this is undesirable, developers can use the cupertinoOverrideTheme property of ThemeData to provide a Cupertino-specific override using a CupertinoThemeData object, e.g: - +CupertinoTextField's cursorColor default now matches the app's theme. If this is undesirable, developers can use the cupertinoOverrideTheme property of ThemeData to provide a Cupertino-specific override using a CupertinoThemeData object, e.g: + ```dart -Widget build(BuildContext context) { - // Set theme data for override in the CupertinoThemeData's constructor - Theme.of(context).cupertinoOverrideTheme = CupertinoThemeData( - brightness: Brightness.dark, - primaryColor: Color(0xFF42A5F5) - ); - return Text( - 'Example', - style: Theme.of(context).textTheme.title, - ); +Widget build(BuildContext context) { + // Set theme data for override in the CupertinoThemeData's constructor + Theme.of(context).cupertinoOverrideTheme = CupertinoThemeData( + brightness: Brightness.dark, + primaryColor: Color(0xFF42A5F5) + ); + return Text( + 'Example', + style: Theme.of(context).textTheme.title, + ); } ``` @@ -207,19 +207,19 @@ Widget build(BuildContext context) { By default, a drag gesture detector's onStart callback will be called with the location of where a drag gesture is detected (i.e. after dragging a certain number of pixels) instead of at the touch down location. To use the old functionality with a given drag gesture recognizer, the dragStartBehavior variable of the recognizer should be set DragStartBehavior.down, e.g., include the bolded line below when declaring your GestureDecorator: ```dart -GestureDectector( +GestureDectector( dragStartBehavior: DragStartBehavior.down, - onVerticalDragDown: myDragDown - onVerticalDragEnd: myDragEnd, - onVerticalDragStart: myDragStart, - onVerticalDragUpdate: myDragUpdate, - onVerticalDragCancel: myDragCancel, - onHorizontalDragDown: myDragDown - onHorizontalDragEnd: myDragEnd, - onHorizontalDragStart: myDragStart, - onHorizontalDragUpdate: myDragUpdate, - onHorizontalDragCancel: myDragCancel, -// Other fields… + onVerticalDragDown: myDragDown + onVerticalDragEnd: myDragEnd, + onVerticalDragStart: myDragStart, + onVerticalDragUpdate: myDragUpdate, + onVerticalDragCancel: myDragCancel, + onHorizontalDragDown: myDragDown + onHorizontalDragEnd: myDragEnd, + onHorizontalDragStart: myDragStart, + onHorizontalDragUpdate: myDragUpdate, + onHorizontalDragCancel: myDragCancel, +// Other fields… ``` @@ -233,9 +233,9 @@ Removed the long-deprecated TwoLevelList widget; use ListView with ExpansionTile Picture.toImage now returns a `Future` instead. This permits image rasterization to occur on the GPU thread, improving performance in many cases and ensuring correct results. At a minimum, you'll need to declare methods invoking on Picture instances as async, and use await, like this: ```dart -void usePictureImage(Picture p) async { - var image = await p.toImage(); - // Do something with the pixels in image…. +void usePictureImage(Picture p) async { + var image = await p.toImage(); + // Do something with the pixels in image…. } ``` diff --git a/src/content/release/release-notes/release-notes-1.20.0.md b/src/content/release/release-notes/release-notes-1.20.0.md index 9a149a0ac46..90fa5e615ba 100644 --- a/src/content/release/release-notes/release-notes-1.20.0.md +++ b/src/content/release/release-notes/release-notes-1.20.0.md @@ -7,7 +7,7 @@ skipTemplateRendering: true ## Merged pull requests by label -### Merged PRs by labels for `flutter/flutter` +### Merged PRs by labels for `flutter/flutter` #### tool - 435 pull request(s) @@ -5602,7 +5602,7 @@ skipTemplateRendering: true [57037](https://github.com/flutter/flutter/pull/57037) Making DropdownButtonFormField to re-render if parent widget changes (cla: yes, f: material design, found in release: 1.17, found in release: 1.18, framework, severe: regression, waiting for tree to go green) -### Merged PRs by labels for `flutter/engine` +### Merged PRs by labels for `flutter/engine` #### platform-android - 62 pull request(s) @@ -6106,7 +6106,7 @@ skipTemplateRendering: true -### Merged PRs by labels for `flutter/plugins` +### Merged PRs by labels for `flutter/plugins` #### submit queue - 3 pull request(s) @@ -8649,7 +8649,7 @@ There were 1243 pull requests. [61129](https://github.com/flutter/flutter/pull/61129) [flutter_tools] fix recursive asset variant issue (cla: yes, tool, waiting for tree to go green) -### Merged PRs in `flutter/engine` +### Merged PRs in `flutter/engine` There were 1316 pull requests. @@ -11594,4 +11594,3 @@ There were 153 pull requests. [2864](https://github.com/flutter/plugins/pull/2864) [shared_preferences] Shared preferences linux endorsement (cla: yes) [2865](https://github.com/flutter/plugins/pull/2865) [shared_preferences_linux] Add iOS stub (cla: yes) - diff --git a/src/content/release/release-notes/release-notes-1.22.0.md b/src/content/release/release-notes/release-notes-1.22.0.md index 9694e0e6a30..a7739559f95 100644 --- a/src/content/release/release-notes/release-notes-1.22.0.md +++ b/src/content/release/release-notes/release-notes-1.22.0.md @@ -10,7 +10,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] [CHANGELOG]: https://github.com/flutter/flutter/blob/master/CHANGELOG.md -## Merged PRs by labels for `flutter/flutter` +## Merged PRs by labels for `flutter/flutter` ### framework - 428 pull request(s) @@ -4694,7 +4694,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] -## Merged PRs by labels for `flutter/engine` +## Merged PRs by labels for `flutter/engine` @@ -5356,7 +5356,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] -## Merged PRs by labels for `flutter/plugins` +## Merged PRs by labels for `flutter/plugins` @@ -8055,5 +8055,3 @@ There were 65 pull requests. [3010](https://github.com/flutter/plugins/pull/3010) [url_launcher_web] Fix a typo in a test name and fix quote consistency (cla: yes, waiting for test harness) [3011](https://github.com/flutter/plugins/pull/3011) [google_maps_flutter] Fix typo in google_maps_flutter/example/map_ui.dart (cla: yes) - - diff --git a/src/content/release/release-notes/release-notes-1.5.4.md b/src/content/release/release-notes/release-notes-1.5.4.md index e500c201913..de156ceb2a8 100644 --- a/src/content/release/release-notes/release-notes-1.5.4.md +++ b/src/content/release/release-notes/release-notes-1.5.4.md @@ -55,7 +55,7 @@ In this release, we fixed several severe performance and crash issues. ## iOS Changes -Supporting iOS is just as important to the Flutter team as support Android, which you can see in the huge volume of changes we've made in this release to make the iOS experience even better. +Supporting iOS is just as important to the Flutter team as support Android, which you can see in the huge volume of changes we've made in this release to make the iOS experience even better. diff --git a/src/content/release/release-notes/release-notes-1.9.1.md b/src/content/release/release-notes/release-notes-1.9.1.md index 74e9eba3c2b..ebef04ce66d 100644 --- a/src/content/release/release-notes/release-notes-1.9.1.md +++ b/src/content/release/release-notes/release-notes-1.9.1.md @@ -109,7 +109,7 @@ This release also brings with it two new Material widgets: the ToggleButtons wid ## macOS Catalina Support -With the release of macOS Catalina just around the corner, we've made sure that our tooling continues to work smoothly as you migrate to Catalina, iOS 13 and Xcode 11. I should note that **you'll want to upgrade to the Flutter 1.9.1 stable release before upgrading to Catalina**. The other order works, too, but you'll see an error when you do it that way (the [error](https://github.com/flutter/flutter/issues/33890) is benign, but still…). +With the release of macOS Catalina just around the corner, we've made sure that our tooling continues to work smoothly as you migrate to Catalina, iOS 13 and Xcode 11. I should note that **you'll want to upgrade to the Flutter 1.9.1 stable release before upgrading to Catalina**. The other order works, too, but you'll see an error when you do it that way (the [error](https://github.com/flutter/flutter/issues/33890) is benign, but still…). [38325](https://github.com/flutter/flutter/pull/38325) refactor flutter upgrade to be 2 part, with the second part re-entrant diff --git a/src/content/release/release-notes/release-notes-2.0.0.md b/src/content/release/release-notes/release-notes-2.0.0.md index 19d253fb646..b92908d5a27 100644 --- a/src/content/release/release-notes/release-notes-2.0.0.md +++ b/src/content/release/release-notes/release-notes-2.0.0.md @@ -10,7 +10,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] [CHANGELOG]: https://github.com/flutter/flutter/blob/master/CHANGELOG.md -## Merged PRs by labels for `flutter/flutter` +## Merged PRs by labels for `flutter/flutter` ### framework - 793 pull request(s) @@ -9380,7 +9380,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] -## Merged PRs by labels for `flutter/engine` +## Merged PRs by labels for `flutter/engine` @@ -20255,7 +20255,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] -## Merged PRs by labels for `flutter/plugins` +## Merged PRs by labels for `flutter/plugins` @@ -29351,4 +29351,3 @@ There were 253 pull requests. [3463](https://github.com/flutter/plugins/pull/3463) bump vmservice (cla: yes, p: integration_test) [3465](https://github.com/flutter/plugins/pull/3465) [path_provider] drop uuid (cla: yes, p: path_provider) - diff --git a/src/content/release/release-notes/release-notes-2.10.0.md b/src/content/release/release-notes/release-notes-2.10.0.md index 54ef1f5303a..1fb9fc8661e 100644 --- a/src/content/release/release-notes/release-notes-2.10.0.md +++ b/src/content/release/release-notes/release-notes-2.10.0.md @@ -10,7 +10,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] [CHANGELOG]: https://github.com/flutter/flutter/blob/master/CHANGELOG.md -## Merged PRs by labels for `flutter/flutter` +## Merged PRs by labels for `flutter/flutter` @@ -5421,7 +5421,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] -## Merged PRs by labels for `flutter/engine` +## Merged PRs by labels for `flutter/engine` @@ -9768,7 +9768,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] -## Merged PRs by labels for `flutter/plugins` +## Merged PRs by labels for `flutter/plugins` @@ -11212,6 +11212,3 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] #### customer: marketplace - 1 pull request(s) [3325](https://github.com/flutter/plugins/pull/3325) [webview_flutter]Add zoom to android webview (cla: yes, waiting for tree to go green, p: webview_flutter, last mile, customer: marketplace) - - - diff --git a/src/content/release/release-notes/release-notes-2.2.0.md b/src/content/release/release-notes/release-notes-2.2.0.md index 517c6c6d4da..0763d94e89d 100644 --- a/src/content/release/release-notes/release-notes-2.2.0.md +++ b/src/content/release/release-notes/release-notes-2.2.0.md @@ -10,7 +10,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] [CHANGELOG]: https://github.com/flutter/flutter/blob/master/CHANGELOG.md -## Merged PRs by labels for `flutter/flutter` +## Merged PRs by labels for `flutter/flutter` ### framework - 397 pull request(s) @@ -5324,7 +5324,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] -## Merged PRs by labels for `flutter/engine` +## Merged PRs by labels for `flutter/engine` @@ -11181,7 +11181,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] -## Merged PRs by labels for `flutter/plugins` +## Merged PRs by labels for `flutter/plugins` @@ -17602,4 +17602,3 @@ There were 274 pull requests. [3790](https://github.com/flutter/plugins/pull/3790) [google_map_flutter] fix version (cla: yes, p: google_maps_flutter) [3791](https://github.com/flutter/plugins/pull/3791) [flutter_webview] Fix `allowsInlineMediaPlayback` ignored on iOS (cla: yes, p: webview_flutter, platform-ios, waiting for tree to go green) - diff --git a/src/content/release/release-notes/release-notes-2.5.0.md b/src/content/release/release-notes/release-notes-2.5.0.md index 151be815673..a5035a10f75 100644 --- a/src/content/release/release-notes/release-notes-2.5.0.md +++ b/src/content/release/release-notes/release-notes-2.5.0.md @@ -10,7 +10,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] [CHANGELOG]: https://github.com/flutter/flutter/blob/master/CHANGELOG.md -## Merged PRs by labels for `flutter/flutter` +## Merged PRs by labels for `flutter/flutter` ### framework - 530 pull request(s) @@ -7569,7 +7569,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] -### Merged PRs by labels for `flutter/engine` +### Merged PRs by labels for `flutter/engine` @@ -18498,7 +18498,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] -### Merged PRs by labels for `flutter/plugins` +### Merged PRs by labels for `flutter/plugins` @@ -22207,6 +22207,3 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] ### in review - 1 pull request(s) [3780](https://github.com/flutter/plugins/pull/3780) [local_auth] Fix iOS crash when no localizedReason (cla: yes, in review, waiting for tree to go green, p: local_auth) - - - diff --git a/src/content/release/release-notes/release-notes-2.8.0.md b/src/content/release/release-notes/release-notes-2.8.0.md index 867e196fb6d..af668a90809 100644 --- a/src/content/release/release-notes/release-notes-2.8.0.md +++ b/src/content/release/release-notes/release-notes-2.8.0.md @@ -10,7 +10,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] [CHANGELOG]: https://github.com/flutter/flutter/blob/master/CHANGELOG.md -## Merged PRs by labels for `flutter/flutter` +## Merged PRs by labels for `flutter/flutter` @@ -9961,7 +9961,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] -## Merged PRs by labels for `flutter/engine` +## Merged PRs by labels for `flutter/engine` @@ -17530,7 +17530,7 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] -## Merged PRs by labels for `flutter/plugins` +## Merged PRs by labels for `flutter/plugins` @@ -19740,6 +19740,3 @@ For information about subsequent bug-fix releases, see our [CHANGELOG][] #### bugfix - 1 pull request(s) [4301](https://github.com/flutter/plugins/pull/4301) [camera] Ensure setExposureOffset returns new value on Android (bugfix, cla: yes, waiting for tree to go green, p: camera, platform-android) - - - diff --git a/src/content/release/release-notes/release-notes-3.10.0.md b/src/content/release/release-notes/release-notes-3.10.0.md index 861e562e29d..9794efa9122 100644 --- a/src/content/release/release-notes/release-notes-3.10.0.md +++ b/src/content/release/release-notes/release-notes-3.10.0.md @@ -2795,4 +2795,3 @@ see our [CHANGELOG][]. * @thomasgales made their first contribution in [40736](https://github.com/flutter/engine/pull/40736) **Full Changelog**: https://github.com/flutter/engine/compare/3.7.4...3.10.0-1.5.pre - diff --git a/src/content/release/release-notes/release-notes-3.13.0.md b/src/content/release/release-notes/release-notes-3.13.0.md index bab38ee2206..bde21a7ef71 100644 --- a/src/content/release/release-notes/release-notes-3.13.0.md +++ b/src/content/release/release-notes/release-notes-3.13.0.md @@ -812,4 +812,3 @@ see our [CHANGELOG][]. * @mk12 made their first contribution in [42667](https://github.com/flutter/engine/pull/42667) **Full Changelog**: https://github.com/flutter/engine/compare/3.10.0...3.13.0 - diff --git a/src/content/release/release-notes/release-notes-archive.md b/src/content/release/release-notes/release-notes-archive.md index 37ac71d0e07..559486e88ed 100644 --- a/src/content/release/release-notes/release-notes-archive.md +++ b/src/content/release/release-notes/release-notes-archive.md @@ -6,7 +6,7 @@ description: Archived release notes for Flutter for prior releases. This page preserves the links to older release notes for releases to the stable channel and -the old GitHub _Changelog_ wiki page before that, +the old GitHub _Changelog_ wiki page before that, which documented changes up through and including 1.0.0. ## Archived release notes diff --git a/src/content/release/whats-new.md b/src/content/release/whats-new.md index 757534e5190..5576420f4c9 100644 --- a/src/content/release/whats-new.md +++ b/src/content/release/whats-new.md @@ -138,4 +138,3 @@ For past releases, check out the [What's new archive][] page. [What's new archive]: /release/archive-whats-new - diff --git a/src/content/resources/architectural-overview.md b/src/content/resources/architectural-overview.md index ad9f3ccd417..79b635bb31c 100644 --- a/src/content/resources/architectural-overview.md +++ b/src/content/resources/architectural-overview.md @@ -593,7 +593,7 @@ such as Windows or macOS. :::note If you want to know which devices Impeller supports, check out [Can I use Impeller?][]. -For more information, +For more information, visit [Impeller rendering engine][] ::: diff --git a/src/content/resources/bug-reports.md b/src/content/resources/bug-reports.md index 211c25e696e..ebbca0bd41a 100644 --- a/src/content/resources/bug-reports.md +++ b/src/content/resources/bug-reports.md @@ -1,7 +1,7 @@ --- title: Create useful bug reports description: > - Where to file bug reports and enhancement requests for + Where to file bug reports and enhancement requests for flutter and the website. showBreadcrumbs: false --- diff --git a/src/content/resources/courses.md b/src/content/resources/courses.md index 44a732197f8..1fd9e583e58 100644 --- a/src/content/resources/courses.md +++ b/src/content/resources/courses.md @@ -28,7 +28,7 @@ To include your course, [submit a PR][]: * [Sticky Grouped Headers in Flutter][] by Marco Napoli * [Flutter University - From Zero to Mastery][] by Fudeo (Italian) * [Tech Idara - Flutter from Basic to Advanced][] by Ishaq Hassan (Urdu) - + [20 Hour Dart & Flutter YT Course For Beginners]: https://youtu.be/CzRQ9mnmh44 [The Best Flutter Course on the Internet]: https://www.hungrimind.com/learn/flutter [Flutter in Production]: https://codewithandrea.com/courses/flutter-in-production/ diff --git a/src/content/resources/faq.md b/src/content/resources/faq.md index c5ccadc8798..4360fcee511 100644 --- a/src/content/resources/faq.md +++ b/src/content/resources/faq.md @@ -481,7 +481,7 @@ On ARM32, the core engine is approximately 3.4 MB (compressed), and necessary Java code (`classes.dex`) is 120 KB (compressed). -In ARM64, the core engine is approximately 4.0 MB +In ARM64, the core engine is approximately 4.0 MB (compressed), the framework + app code is approximately 659 KB (compressed), the LICENSE file is 58 KB (compressed), and necessary Java code (`classes.dex`) @@ -521,7 +521,7 @@ To do that, see [Measuring your app's size][]. Flutter uses logical pixels, and often refers to them merely as "pixels". Flutter's [`devicePixelRatio`][] expresses the ratio -between physical pixels and logical CSS pixels. +between physical pixels and logical CSS pixels. [`devicePixelRatio`]: {{site.api}}/flutter/dart-html/Window/devicePixelRatio.html @@ -581,7 +581,7 @@ You can compile and deploy your Flutter app to iOS, Android, * We support and test running Flutter on a variety of low-end to high-end platforms. For a detailed list - of the platforms on which we test, see + of the platforms on which we test, see the list of [supported platforms][]. * Flutter supports building ahead-of-time (AOT) compiled libraries diff --git a/src/content/resources/games-toolkit.md b/src/content/resources/games-toolkit.md index b28dd46f604..a4c2813839f 100644 --- a/src/content/resources/games-toolkit.md +++ b/src/content/resources/games-toolkit.md @@ -79,7 +79,7 @@ The Casual Games Toolkit provides the following free resources. 1. A sample game built on top of the endless runner template, called SuperDash. You can play the game on iOS, Android, - or [web][], [view the open source code repo][], or + or [web][], [view the open source code repo][], or [read how the game was created in 6 weeks][]. * Developer guides for integrating needed services. diff --git a/src/content/security/index.md b/src/content/security/index.md index c811df67478..cba100c7b37 100644 --- a/src/content/security/index.md +++ b/src/content/security/index.md @@ -15,7 +15,7 @@ the risk of introducing a vulnerability. Flutter security strategy is based on five key pillars: * **Identify**: Track and prioritize key security risks by - identifying core assets, key threats, and vulnerabilities. + identifying core assets, key threats, and vulnerabilities. * **Detect**: Detect and identify vulnerabilities using techniques and tools like vulnerability scanning, static application security testing, and fuzzing. @@ -103,7 +103,7 @@ Google considers Flutter to be in scope for the ## Receiving security updates -The best way to receive security updates is to subscribe to the +The best way to receive security updates is to subscribe to the [flutter-announce][] mailing list or watch updates to the [Discord channel][]. We also announce security updates in the technical release blog post. @@ -135,4 +135,3 @@ technical release blog post. changes with the community. [upgrade your package dependencies]: /install/upgrade - diff --git a/src/content/testing/code-debugging.md b/src/content/testing/code-debugging.md index b83a568161b..7ac5f045e71 100644 --- a/src/content/testing/code-debugging.md +++ b/src/content/testing/code-debugging.md @@ -673,7 +673,7 @@ segments of Dart code, use `dart:developer` [Timeline][] utilities. ```dart import 'dart:developer'; - + void main() { Timeline.startSync('interesting function'); // iWonderHowLongThisTakes(); diff --git a/src/content/testing/common-errors.md b/src/content/testing/common-errors.md index 17d0ae11b2e..e24ec494ba3 100644 --- a/src/content/testing/common-errors.md +++ b/src/content/testing/common-errors.md @@ -65,8 +65,8 @@ The relevant error-causing widget was Row lib/errors/renderflex_overflow_column.dart:23 The overflowing RenderFlex has an orientation of Axis.horizontal. -The edge of the RenderFlex that is overflowing has been marked in the rendering -with a yellow and black striped pattern. This is usually caused by the contents +The edge of the RenderFlex that is overflowing has been marked in the rendering +with a yellow and black striped pattern. This is usually caused by the contents being too big for the RenderFlex. (Additional lines of this message omitted) ``` @@ -179,7 +179,7 @@ occurring earlier in the rendering pipeline. The message shown by the error looks like this: ```plaintext -RenderBox was not laid out: +RenderBox was not laid out: RenderViewport#5a477 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE ``` @@ -212,9 +212,9 @@ The message shown by the error looks like this: The following assertion was thrown during performResize(): Vertical viewport was given unbounded height. -Viewports expand in the scrolling direction to fill their container. -In this case, a vertical viewport was given an unlimited amount of -vertical space in which to expand. This situation typically happens when a +Viewports expand in the scrolling direction to fill their container. +In this case, a vertical viewport was given an unlimited amount of +vertical space in which to expand. This situation typically happens when a scrollable widget is nested inside another scrollable widget. (Additional lines of this message omitted) ``` @@ -299,11 +299,11 @@ The message shown by the error looks like this: ```plaintext The following assertion was thrown during performLayout(): -An InputDecorator, which is typically created by a TextField, cannot have an +An InputDecorator, which is typically created by a TextField, cannot have an unbounded width. -This happens when the parent widget does not provide a finite width constraint. -For example, if the InputDecorator is contained by a `Row`, then its width must -be constrained. An `Expanded` widget or a SizedBox can be used to constrain the +This happens when the parent widget does not provide a finite width constraint. +For example, if the InputDecorator is contained by a `Row`, then its width must +be constrained. An `Expanded` widget or a SizedBox can be used to constrain the width of the InputDecorator or the TextField that contains it. (Additional lines of this message omitted) ``` @@ -357,7 +357,7 @@ The message shown by the error looks like this: The following assertion was thrown while looking for parent data: Incorrect use of ParentDataWidget. (Some lines of this message omitted) -Usually, this indicates that at least one of the offending ParentDataWidgets +Usually, this indicates that at least one of the offending ParentDataWidgets listed above is not placed directly inside a compatible ancestor widget. ``` @@ -398,12 +398,12 @@ When the error occurs, the following message is displayed in the console: ```plaintext -The following assertion was thrown building DialogPage(dirty, dependencies: -[_InheritedTheme, _LocalizationsScope-[GlobalKey#59a8e]], +The following assertion was thrown building DialogPage(dirty, dependencies: +[_InheritedTheme, _LocalizationsScope-[GlobalKey#59a8e]], state: _DialogPageState#f121e): setState() or markNeedsBuild() called during build. -This Overlay widget cannot be marked as needing to build because the framework +This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets. (Additional lines of this message omitted) ``` diff --git a/src/content/testing/debugging.md b/src/content/testing/debugging.md index 910e0cf111f..0c34b21fad4 100644 --- a/src/content/testing/debugging.md +++ b/src/content/testing/debugging.md @@ -8,7 +8,7 @@ description: How to debug your Flutter app. There's a wide variety of tools and features to help debug Flutter applications. Here are some of the available tools: -* [VS Code][] (recommended) and [Android Studio/IntelliJ][], +* [VS Code][] (recommended) and [Android Studio/IntelliJ][], (enabled with the Flutter and Dart plugins) support a built-in source-level debugger with the ability to set breakpoints, step through code, diff --git a/src/content/testing/integration-tests/index.md b/src/content/testing/integration-tests/index.md index ea0aeea5e1e..c8b17e61665 100644 --- a/src/content/testing/integration-tests/index.md +++ b/src/content/testing/integration-tests/index.md @@ -207,29 +207,29 @@ and your app's Dart file. import 'package:flutter_test/flutter_test.dart'; import 'package:how_to/main.dart'; import 'package:integration_test/integration_test.dart'; - + void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - + group('end-to-end test', () { testWidgets('tap on the floating action button, verify counter', ( tester, ) async { // Load app widget. await tester.pumpWidget(const MyApp()); - + // Verify the counter starts at 0. expect(find.text('0'), findsOneWidget); - + // Finds the floating action button to tap on. final fab = find.byKey(const ValueKey('increment')); - + // Emulate a tap on the floating action button. await tester.tap(fab); - + // Trigger a frame. await tester.pumpAndSettle(); - + // Verify the counter increments by 1. expect(find.text('1'), findsOneWidget); }); @@ -438,7 +438,7 @@ To test in a web browser, perform the following steps. ```dart title="test_driver/integration_test.dart" import 'package:integration_test/integration_test_driver.dart'; - + Future main() => integrationDriver(); ``` diff --git a/src/content/testing/testing-plugins.md b/src/content/testing/testing-plugins.md index f210fe13dbf..fe2a32956c5 100644 --- a/src/content/testing/testing-plugins.md +++ b/src/content/testing/testing-plugins.md @@ -206,4 +206,3 @@ Some extra considerations for plugin testing: [general advice]: /testing/overview [Visual Studio test UI]: https://learn.microsoft.com/en-us/visualstudio/test/getting-started-with-unit-testing?view=vs-2022&tabs=dotnet%2Cmstest#run-unit-tests [Xcode Test UI]: {{site.apple-dev}}/library/archive/documentation/DeveloperTools/Conceptual/testing_with_xcode/chapters/05-running_tests.html - diff --git a/src/content/testing/vscode-flutter-bar/_hot-reload.md b/src/content/testing/vscode-flutter-bar/_hot-reload.md index 5741cbad413..33786b5e6ed 100644 --- a/src/content/testing/vscode-flutter-bar/_hot-reload.md +++ b/src/content/testing/vscode-flutter-bar/_hot-reload.md @@ -1 +1 @@ -![Small yellow lightning bolt that indicates reloading the UI of a Flutter app without resetting any state values](/assets/images/docs/testing/debugging/vscode-ui/icons/hot-reload.png) \ No newline at end of file +![Small yellow lightning bolt that indicates reloading the UI of a Flutter app without resetting any state values](/assets/images/docs/testing/debugging/vscode-ui/icons/hot-reload.png) diff --git a/src/content/testing/vscode-flutter-bar/_hot-restart.md b/src/content/testing/vscode-flutter-bar/_hot-restart.md index ff89dd538b6..ece267dd14e 100644 --- a/src/content/testing/vscode-flutter-bar/_hot-restart.md +++ b/src/content/testing/vscode-flutter-bar/_hot-restart.md @@ -1 +1 @@ -![Small green almost circular arrow that indicates reloading the UI of a Flutter app and resetting any state values](/assets/images/docs/testing/debugging/vscode-ui/icons/hot-restart.png) \ No newline at end of file +![Small green almost circular arrow that indicates reloading the UI of a Flutter app and resetting any state values](/assets/images/docs/testing/debugging/vscode-ui/icons/hot-restart.png) diff --git a/src/content/testing/vscode-flutter-bar/_inspector.md b/src/content/testing/vscode-flutter-bar/_inspector.md index 58a9259ba5a..7a44518fc2c 100644 --- a/src/content/testing/vscode-flutter-bar/_inspector.md +++ b/src/content/testing/vscode-flutter-bar/_inspector.md @@ -1 +1 @@ -![Small blue magnifying class with the Flutter logo inside it that opens the Widget inspector](/assets/images/docs/testing/debugging/vscode-ui/icons/inspector.png) \ No newline at end of file +![Small blue magnifying class with the Flutter logo inside it that opens the Widget inspector](/assets/images/docs/testing/debugging/vscode-ui/icons/inspector.png) diff --git a/src/content/testing/vscode-flutter-bar/_pause.md b/src/content/testing/vscode-flutter-bar/_pause.md index c6d3545c6d6..f4e51c7a592 100644 --- a/src/content/testing/vscode-flutter-bar/_pause.md +++ b/src/content/testing/vscode-flutter-bar/_pause.md @@ -1 +1 @@ -![Small blue double vertical line that indicates pausing the Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/pause.png) \ No newline at end of file +![Small blue double vertical line that indicates pausing the Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/pause.png) diff --git a/src/content/testing/vscode-flutter-bar/_play.md b/src/content/testing/vscode-flutter-bar/_play.md index d12583a55ea..f9489ceeb74 100644 --- a/src/content/testing/vscode-flutter-bar/_play.md +++ b/src/content/testing/vscode-flutter-bar/_play.md @@ -1 +1 @@ -![Small blue vertical line with a blue triangle that indicates playing or resuming the Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/play-or-resume.png) \ No newline at end of file +![Small blue vertical line with a blue triangle that indicates playing or resuming the Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/play-or-resume.png) diff --git a/src/content/testing/vscode-flutter-bar/_step-into.md b/src/content/testing/vscode-flutter-bar/_step-into.md index 0a5e3ea4e9c..1dcd598d190 100644 --- a/src/content/testing/vscode-flutter-bar/_step-into.md +++ b/src/content/testing/vscode-flutter-bar/_step-into.md @@ -1 +1 @@ -![Small blue downward arrow over a blue circle that indicates going into the next function in a Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/step-into.png) \ No newline at end of file +![Small blue downward arrow over a blue circle that indicates going into the next function in a Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/step-into.png) diff --git a/src/content/testing/vscode-flutter-bar/_step-out.md b/src/content/testing/vscode-flutter-bar/_step-out.md index 3083a152a06..827bd97a26f 100644 --- a/src/content/testing/vscode-flutter-bar/_step-out.md +++ b/src/content/testing/vscode-flutter-bar/_step-out.md @@ -1 +1 @@ -![Small blue upward arrow over a blue circle that indicates exiting the current function after one passthrough in a Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/step-out.png) \ No newline at end of file +![Small blue upward arrow over a blue circle that indicates exiting the current function after one passthrough in a Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/step-out.png) diff --git a/src/content/testing/vscode-flutter-bar/_step-over.md b/src/content/testing/vscode-flutter-bar/_step-over.md index 9466e79886f..b0ac5b16b46 100644 --- a/src/content/testing/vscode-flutter-bar/_step-over.md +++ b/src/content/testing/vscode-flutter-bar/_step-over.md @@ -1 +1 @@ -![Small blue arched arrow over a blue circle that indicates skipping the current block or statement in the Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/step-over.png) \ No newline at end of file +![Small blue arched arrow over a blue circle that indicates skipping the current block or statement in the Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/step-over.png) diff --git a/src/content/testing/vscode-flutter-bar/_stop.md b/src/content/testing/vscode-flutter-bar/_stop.md index df7353db4f9..13e43a9b375 100644 --- a/src/content/testing/vscode-flutter-bar/_stop.md +++ b/src/content/testing/vscode-flutter-bar/_stop.md @@ -1 +1 @@ -![Red empty square that indicates you want to stop the running Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/stop.png) \ No newline at end of file +![Red empty square that indicates you want to stop the running Flutter app](/assets/images/docs/testing/debugging/vscode-ui/icons/stop.png) diff --git a/src/content/tools/devtools/cli.md b/src/content/tools/devtools/cli.md index 388a54ef156..5a337f071cb 100644 --- a/src/content/tools/devtools/cli.md +++ b/src/content/tools/devtools/cli.md @@ -51,9 +51,9 @@ http://127.0.0.1:9100?uri=http://127.0.0.1:51830/u37pq71Re0k=/ Open the DevTools instance connected to your app by opening the second link in Chrome. -This URL contains a security token, -so it's different for each run of your app. -This means that if you stop your application and re-run it, +This URL contains a security token, +so it's different for each run of your app. +This means that if you stop your application and re-run it, you need to connect to DevTools again with the new URL. ## Connect to a new app instance diff --git a/src/content/tools/devtools/console.md b/src/content/tools/devtools/console.md index 5150ba74ea5..e7f0147ac0a 100644 --- a/src/content/tools/devtools/console.md +++ b/src/content/tools/devtools/console.md @@ -29,7 +29,7 @@ The console shows the application's standard output (`stdout`): ## Explore inspected widgets If you click a widget on the **Inspector** screen, -the variable for this widget displays in the **Console**: +the variable for this widget displays in the **Console**: ![Screenshot of inspected widget in Console view](/assets/images/docs/tools/devtools/console-inspect-widget.png){:width="100%"} @@ -65,4 +65,3 @@ The Console screen displays both live and static inbound and outbound references, as well as field values: ![Screenshot showing inbound and outbound references in Console](/assets/images/docs/tools/devtools/console-references.png){:width="100%"} - diff --git a/src/content/tools/devtools/cpu-profiler.md b/src/content/tools/devtools/cpu-profiler.md index 385d1a3c676..4fbeadcdf3d 100644 --- a/src/content/tools/devtools/cpu-profiler.md +++ b/src/content/tools/devtools/cpu-profiler.md @@ -6,7 +6,7 @@ description: Learn how to use the DevTools CPU profiler view. :::note The CPU profiler view works with Dart CLI and mobile apps only. Use Chrome DevTools to [analyze performance][] -of a web app. +of a web app. ::: The CPU profiler view allows you to record and profile a @@ -209,9 +209,9 @@ This adds vertical guidelines between parent and child in the tree. ![Screenshot of display options](/assets/images/docs/tools/devtools/display-options.png) [analyze performance]: {{site.developers}}/web/tools/chrome-devtools/evaluate-performance/ - + ## Other resources - + To learn how to use DevTools to analyze the CPU usage of a compute-intensive Mandelbrot app, check out a guided [CPU Profiler View tutorial][profiler-tutorial]. diff --git a/src/content/tools/devtools/deep-links.md b/src/content/tools/devtools/deep-links.md index 4bc0583f103..a71044159a8 100644 --- a/src/content/tools/devtools/deep-links.md +++ b/src/content/tools/devtools/deep-links.md @@ -27,5 +27,4 @@ This tool helps you identify and troubleshoot any errors in your mobile deep link setup, from website configuration to manifest files. DevTools provides instructions on how to fix any issues, -making the implementation process easier. - +making the implementation process easier. diff --git a/src/content/tools/devtools/extensions.md b/src/content/tools/devtools/extensions.md index 5f3c88b54db..b260629f12e 100644 --- a/src/content/tools/devtools/extensions.md +++ b/src/content/tools/devtools/extensions.md @@ -24,7 +24,7 @@ shows up in a new tab when you open DevTools. You need to manually enable the extension before it loads for the first time. Make sure the extension is provided by -a source you trust before enabling it. +a source you trust before enabling it. When you open the extension for the first time, you'll see a prompt to enable the extension: diff --git a/src/content/tools/devtools/inspector.md b/src/content/tools/devtools/inspector.md index 8611dc6a063..08e0de12983 100644 --- a/src/content/tools/devtools/inspector.md +++ b/src/content/tools/devtools/inspector.md @@ -33,7 +33,7 @@ As part of Flutter 3.29, the new Flutter inspector is enabled by default. Howeve [inspector settings dialog]: #inspector-settings [legacy inspector]: /tools/devtools/legacy-inspector -[filing a bug]: https://github.com/flutter/devtools/issues/new +[filing a bug]: https://github.com/flutter/devtools/issues/new ### Debugging layout issues visually @@ -99,7 +99,7 @@ see [Understanding constraints][]. ## Flutter Widget Tree -The Flutter Widget Tree allows you to visualize, understand and navigate your app's Widget tree. +The Flutter Widget Tree allows you to visualize, understand and navigate your app's Widget tree. ![Image of Flutter inspector with Widget Tree highlighted](/assets/images/docs/tools/devtools/inspector-widget-tree.png){:width="100%"} @@ -110,7 +110,7 @@ The Flutter Widget Tree allows you to visualize, understand and navigate your ap By default, the Flutter Widget Tree includes all the widgets created in your root project's directory. -The parent-children relationships of the widgets are represented by a single vertical line (if the parent widget only has a single child) or through +The parent-children relationships of the widgets are represented by a single vertical line (if the parent widget only has a single child) or through indentation (if the parent widget has multiple children.) For example, for the following section of a widget tree: @@ -125,7 +125,7 @@ For example, for the following section of a widget tree: #### Viewing all widgets To instead view all the widgets in your widget tree, including -those that were created outside of your project, toggle on "Show implementation widgets". +those that were created outside of your project, toggle on "Show implementation widgets". The implementation widgets are shown in a lighter font than the widgets created in your project, thereby visually distinguishing them. They are also hidden behind collapsible groups @@ -567,11 +567,11 @@ Toggling this value enables or disables the hover inspection functionality. ### Enable widget tree auto-refreshing When enabled, the widget tree automatically refreshes after -a hot-reload or a navigation event. +a hot-reload or a navigation event. ### Use legacy inspector -When enabled, use the [legacy inspector][] instead of the new inspector. +When enabled, use the [legacy inspector][] instead of the new inspector. :::note The [legacy inspector][] will be removed in a future release. @@ -582,7 +582,7 @@ Let us know if there are issues preventing you from using the new inspector by [ ### Package directories -By default, DevTools limits the widgets displayed in the widget tree to those created +By default, DevTools limits the widgets displayed in the widget tree to those created in the project's root directory. To see all widgets, including those created outside of a the project's root directory, toggle on [Show implementation widgets][] diff --git a/src/content/tools/devtools/legacy-inspector.md b/src/content/tools/devtools/legacy-inspector.md index f807867b443..f85395448c7 100644 --- a/src/content/tools/devtools/legacy-inspector.md +++ b/src/content/tools/devtools/legacy-inspector.md @@ -10,7 +10,7 @@ Please note that the legacy inspector will be removed in a future release. Let u ::: [new inspector]: /tools/devtools/inspector -[filing a bug]: https://github.com/flutter/devtools/issues/new +[filing a bug]: https://github.com/flutter/devtools/issues/new ## The legacy Flutter inspector diff --git a/src/content/tools/devtools/performance.md b/src/content/tools/devtools/performance.md index de1f7aa741a..d9589d2bd30 100644 --- a/src/content/tools/devtools/performance.md +++ b/src/content/tools/devtools/performance.md @@ -225,13 +225,13 @@ to the jank you saw in your app. is affecting performance. If performance improves with this option disabled, try to reduce the use of clipping effects in your app. - + **Render Opacity layers** : Disable this option to check whether excessive use of opacity effects are affecting performance. If performance improves with this option disabled, try to reduce the use of opacity effects in your app. - + **Render Physical Shape layers** : Disable this option to check whether excessive use of physical modeling effects are affecting performance, diff --git a/src/content/tools/property-editor.md b/src/content/tools/property-editor.md index ccacac2c001..35dcd8a7a55 100644 --- a/src/content/tools/property-editor.md +++ b/src/content/tools/property-editor.md @@ -27,7 +27,7 @@ UI development and iteration. 2. Locate a [widget constructor invocation][] in your Flutter code. -3. Move your cursor anywhere inside the widget constructor invocation. +3. Move your cursor anywhere inside the widget constructor invocation. For example, in the following `build` method, place your cursor anywhere between the `T` of `Text` and the ending parenthesis `)` after @@ -103,9 +103,9 @@ with hot reload to view changes in real time. * Open `Settings > Tools > Actions on Save` and select `Configure autosave options`. - Check the option to `Save files if the IDE is idle for X seconds`. - - **Recommended:** Set a small delay duration. For example, 2 seconds. - - * Open `Settings > Languages & Frameworks > Flutter`. + - **Recommended:** Set a small delay duration. For example, 2 seconds. + + * Open `Settings > Languages & Frameworks > Flutter`. - Check the option to `Perform hot reload on save`. 2. Run and debug your Flutter application. @@ -124,7 +124,7 @@ speed up the development process. When a widget is selected in the Flutter Property Editor, its documentation is displayed at the top. This allows you to quickly read the widget documentation, -without needing to jump-to-definition or search online. +without needing to jump-to-definition or search online. By default, the widget documentation is truncated. Click on "Show more" to expand the widget documentation. @@ -211,7 +211,7 @@ properties of interest. `true`. * Typing "double" would filter to all properties of type `double`. - ![Filter input with filtering by text highlighted](/assets/images/docs/tools/devtools/property-editor-filter-text.png){:width="500px"} + ![Filter input with filtering by text highlighted](/assets/images/docs/tools/devtools/property-editor-filter-text.png){:width="500px"} * **Filter by "set" properties:** * Use the filter menu button to open the filter options. Check "Only @@ -219,7 +219,7 @@ properties of interest. * This hides all properties that have not been explicitly set in your code, allowing you to focus only on the properties you have explicitly set. - ![Filter input with filter menu button highlighted](/assets/images/docs/tools/devtools/property-editor-filter-menu-button.png){:width="500px"} + ![Filter input with filter menu button highlighted](/assets/images/docs/tools/devtools/property-editor-filter-menu-button.png){:width="500px"} * **Filter with a regex:** * The regex toggle (an `*` icon button) allows you to toggle on regex mode @@ -227,7 +227,7 @@ properties of interest. * When enabled, your filter text will be interpreted as a regular expression. - ![Filter input with regex toggle highlighted](/assets/images/docs/tools/devtools/property-editor-filter-regex-toggle.png){:width="500px"} + ![Filter input with regex toggle highlighted](/assets/images/docs/tools/devtools/property-editor-filter-regex-toggle.png){:width="500px"} * **Clear the current filter:** * The clear button (an `X` icon button) clears out any active filters, diff --git a/src/content/tools/pubspec.md b/src/content/tools/pubspec.md index 860de7bb591..eae16d4a1ca 100644 --- a/src/content/tools/pubspec.md +++ b/src/content/tools/pubspec.md @@ -4,7 +4,7 @@ description: "Describes the Flutter-only fields in the pubspec file." --- This page is primarily aimed at folks who write -Flutter apps. If you write packages or plugins, +Flutter apps. If you write packages or plugins, (perhaps you want to create a federated plugin), you should check out the [Developing packages and plugins][] page. @@ -113,7 +113,7 @@ dev_dependencies: Flutter-specific and Dart-specific fields can be added to the Flutter pubspec. To learn more about Flutter-specific fields, see the following sections. To learn more about -Dart-specific fields, see [Dart's pubspec supported fields][]. +Dart-specific fields, see [Dart's pubspec supported fields][]. :::note The pubspec can have additional auto-generated Flutter @@ -326,7 +326,7 @@ flutter: To learn more about how you can use deferred components with a Flutter Android app, see -[Deferred components for Android]. +[Deferred components for Android]. [Deferred components for Android]: /perf/deferred-components @@ -413,7 +413,7 @@ flutter: fonts: - asset: fonts/Roboto-Regular.ttf weight: 900 # Optional - style: italic # Optional + style: italic # Optional ``` Use a font family: diff --git a/src/content/tools/sdk.md b/src/content/tools/sdk.md index a7b3684cc46..cc44bc0da99 100644 --- a/src/content/tools/sdk.md +++ b/src/content/tools/sdk.md @@ -44,7 +44,7 @@ The [`dart` CLI tool][] is available with the Flutter SDK at `flutter/bin/dart`. ## SDK support for Flutter developer tools -The IDE tooling for Flutter (Android Studio and Intellij plugins, VS Code +The IDE tooling for Flutter (Android Studio and Intellij plugins, VS Code extensions) supports Flutter SDK versions going back two years. This means that while the tools might still function with SDKs older than two years, they will no longer provide fixes for issues specific to these older versions. diff --git a/src/content/tools/vs-code.md b/src/content/tools/vs-code.md index 9d2963faf04..36093b32b1d 100644 --- a/src/content/tools/vs-code.md +++ b/src/content/tools/vs-code.md @@ -367,7 +367,7 @@ You can also define custom snippets by executing **Hot reload** : To perform a hot reload during a debug session, click **Hot Reload** on the **Debug Toolbar**. - + You can also press Ctrl + F5 (Cmd + F5 on macOS). diff --git a/src/content/tools/widget-previewer.md b/src/content/tools/widget-previewer.md index 5c695e8662e..f46b02e9503 100644 --- a/src/content/tools/widget-previewer.md +++ b/src/content/tools/widget-previewer.md @@ -46,9 +46,9 @@ you must use the [`@Preview`][] annotation defined in can be applied to: * **Top-level functions** that return a `Widget` or - `WidgetBuilder`. + `WidgetBuilder`. * **Static methods** within a class that return a `Widget` or - `WidgetBuilder`. + `WidgetBuilder`. * **Public Widget constructors and factories** with no required arguments. @@ -100,17 +100,17 @@ use to customize the preview: * **`size`**: Artificial size constraints using a `Size` object. - + * **`textScaleFactor`**: A custom font scale. * **`wrapper`**: A function that wraps your previewed widget in a specific widget tree (for example, to inject application state into the widget tree with an `InheritedWidget`). - + * **`theme`**: A function to provide Material and Cupertino theming data. - + * **`brightness`**: The initial theme brightness. * **`localizations`**: A function to apply a localization @@ -129,7 +129,7 @@ should be aware of: implementation to work correctly. The requirement for public variable names will be relaxed in future releases, but function arguments must always have - public names. + public names. * **Unsupported APIs**: Native plugins and any APIs from the `dart:io` library are not supported. This is because @@ -138,7 +138,7 @@ should be aware of: native platform APIs. While web plugins might work when using Chrome, there is no guarantee that they will work within other environments, such as when embedded in - IDEs. + IDEs. * **Asset paths**: When using `fromAsset` APIs from `dart:ui` to load resources, you must use @@ -146,12 +146,12 @@ should be aware of: This ensures that the assets can be correctly located and loaded within the previewer's web environment. For example, use `'packages/my_package_name/assets/my_image.png'` - instead of `'assets/my_image.png'`. + instead of `'assets/my_image.png'`. * **Browser support**: At this time, the previewer is only supported on Chrome as it requires hot reload support. Web server and IDE support for this feature is - planned for a future release. + planned for a future release. * **Unconstrained widgets**: Unconstrained widgets are automatically constrained to approximately half the diff --git a/src/content/tutorial/index.md b/src/content/tutorial/index.md index 17d47b0ffee..6acee389f19 100644 --- a/src/content/tutorial/index.md +++ b/src/content/tutorial/index.md @@ -5,13 +5,13 @@ showToc: false sitemap: false --- -## Welcome! +## Welcome! -Welcome to the Flutter tutorial! This tutorial teaches you how to build +Welcome to the Flutter tutorial! This tutorial teaches you how to build applications from scratch that run on mobile, desktop, and web. -You’ll start from the very beginning: creating a blank Flutter application. -By the end, you’ll have built a handful of small apps that demonstrate +You’ll start from the very beginning: creating a blank Flutter application. +By the end, you’ll have built a handful of small apps that demonstrate the critical features of Flutter development (and more!) {%- comment %} @@ -20,13 +20,13 @@ TODO(ewindmill) welcome video ## What is Flutter? -Flutter is an open-source UI toolkit that helps you build natively compiled, -expressive apps across mobile, web, and desktop from a single codebase. -It’s declarative, reactive, features hot reload for fast development cycles, +Flutter is an open-source UI toolkit that helps you build natively compiled, +expressive apps across mobile, web, and desktop from a single codebase. +It’s declarative, reactive, features hot reload for fast development cycles, and has a rich set of customizable widgets for creating expressive interfaces. -Flutter draws every pixel itself rather than wrapping native components, -giving developers complete control over the UI and ensuring visual consistency +Flutter draws every pixel itself rather than wrapping native components, +giving developers complete control over the UI and ensuring visual consistency across platforms. ## How to use this tutorial @@ -39,13 +39,13 @@ likely be okay.) ## Set up -While reading this tutorial, you’ll ideally be coding along with the examples presented. -You can do so by [installing Flutter on your machine][], +While reading this tutorial, you’ll ideally be coding along with the examples presented. +You can do so by [installing Flutter on your machine][], or by using [Firebase Studio][], a web IDE that supports Flutter. If you’re running locally, this tutorial assumes that you’re running Flutter -apps on the web, using [Chrome][]. This doesn’t require Xcode or Android Studio, -and thus is the quickest way to start using Flutter. +apps on the web, using [Chrome][]. This doesn’t require Xcode or Android Studio, +and thus is the quickest way to start using Flutter. ## Contents @@ -85,4 +85,4 @@ and thus is the quickest way to start using Flutter. [Set up your project]: /tutorial/set-up-ui-102/ [`LayoutBuilder` and adaptive layouts]: /tutorial/adaptive-layouts/ [Scrolling and slivers]: /tutorial/slivers/ -[Stack based navigation]: /tutorial/stack-based-navigation/ \ No newline at end of file +[Stack based navigation]: /tutorial/stack-based-navigation/ diff --git a/src/content/tutorial/state/1-set-up-project.md b/src/content/tutorial/state/1-set-up-project.md index a44927e437b..480290bcea0 100644 --- a/src/content/tutorial/state/1-set-up-project.md +++ b/src/content/tutorial/state/1-set-up-project.md @@ -7,11 +7,11 @@ sitemap: false In this tutorial, you'll learn how to work with data in a Flutter app. You'll build an app that fetches and displays article summaries from -the [Wikipedia API][]. +the [Wikipedia API][]. -A screenshot of the completed 
-Wikipedia reader app showing an article with image, title, 
+<img src= This tutorial explores: @@ -20,7 +20,7 @@ This tutorial explores: * Managing application state with `ChangeNotifier` * Using the MVVM architecture pattern * Creating responsive user interfaces that update automatically when - data changes + data changes This tutorial assumes you've completed the [Dart Getting Started diff --git a/src/content/tutorial/state/2-http-requests.md b/src/content/tutorial/state/2-http-requests.md index 7085cc4dd25..b6ada1f7c06 100644 --- a/src/content/tutorial/state/2-http-requests.md +++ b/src/content/tutorial/state/2-http-requests.md @@ -12,9 +12,9 @@ Model handles data operations, the View displays the UI, and the ViewModel manages state and connects them. 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. +testable, reusable, and easier to maintain. -A diagram that shows the three layers of MVVM architecture: Model, ViewModel, and View. A single feature in your app contains each one of the MVVM components. In @@ -26,7 +26,7 @@ this tutorial, you'll create an `ArticleModel`, `ArticleViewModel` and The Model is the source-of-truth for your app's data, and is responsible for 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. +A model doesn't usually need to import Flutter libraries. Create an empty `ArticleModel` class in your `main.dart` file: @@ -46,7 +46,7 @@ summary. https://en.wikipedia.org/api/rest_v1/page/random/summary ``` -Add a method to fetch random Wikipedia article summaries: +Add a method to fetch random Wikipedia article summaries: ```dart class ArticleModel { @@ -56,7 +56,7 @@ class ArticleModel { '/api/rest_v1/page/random/summary', ); final response = await get(uri); - + // TODO: Add error handling and JSON parsing. } } @@ -128,4 +128,4 @@ tutorial][]. [`async` and `await`]: https://dart.dev/language/async [`Future`]: https://api.dart.dev/stable/dart-async/Future-class.html [Wikipedia API]: https://en.wikipedia.org/api/rest_v1/ -[Dart Getting Started tutorial]: {{site.dart-site}}/tutorial/json \ No newline at end of file +[Dart Getting Started tutorial]: {{site.dart-site}}/tutorial/json diff --git a/src/content/tutorial/state/3-change-notifier.md b/src/content/tutorial/state/3-change-notifier.md index 5150551e5ee..7e9673f338d 100644 --- a/src/content/tutorial/state/3-change-notifier.md +++ b/src/content/tutorial/state/3-change-notifier.md @@ -61,8 +61,8 @@ class ArticleViewModel extends ChangeNotifier { } ``` -This constructor initialization provides immediate content when the -ViewModel is created. Because constructors can't be asynchronous, +This constructor initialization provides immediate content when the +ViewModel is created. Because constructors can't be asynchronous, it delegates initial content fetching to a separate method. ## Create the getRandomArticleSummary method @@ -83,9 +83,9 @@ class ArticleViewModel extends ChangeNotifier { Future getRandomArticleSummary() async { loading = true; notifyListeners(); - + // TODO: Add data fetching logic - + loading = false; notifyListeners(); } @@ -145,10 +145,10 @@ Future getRandomArticleSummary() async { notifyListeners(); try { summary = await model.getRandomArticleSummary(); - print('Article loaded: ${summary!.titles.normalized}'); // Temporary + print('Article loaded: ${summary!.titles.normalized}'); // Temporary errorMessage = null; } on HttpException catch (error) { - print('Error loading article: ${error.message}'); // Temporary + print('Error loading article: ${error.message}'); // Temporary errorMessage = error.message; summary = null; } @@ -168,7 +168,7 @@ class MainApp extends StatelessWidget { Widget build(BuildContext context) { // Create ViewModel to test HTTP requests final viewModel = ArticleViewModel(ArticleModel()); - + return MaterialApp( home: Scaffold( appBar: AppBar( diff --git a/src/content/tutorial/state/4-listenable-builder.md b/src/content/tutorial/state/4-listenable-builder.md index 4c324ec800e..eda57e8d1f5 100644 --- a/src/content/tutorial/state/4-listenable-builder.md +++ b/src/content/tutorial/state/4-listenable-builder.md @@ -91,13 +91,13 @@ class ArticleView extends StatelessWidget { `ListenableBuilder` uses the *builder* pattern, which requires a callback rather than a `child` widget to build the widget tree below it. These widgets are flexible because you can perform operations -within the callback. +within the callback. ## Handle all states with switch expression Recall the `ArticleViewModel`, which has three properties that the UI -is interested in: +is interested in: * `Summary? summary` * `bool loading` * `String? errorMessage` diff --git a/src/content/tutorial/ui-102/1-intro.md b/src/content/tutorial/ui-102/1-intro.md index 10e48f6549e..6a943297a38 100644 --- a/src/content/tutorial/ui-102/1-intro.md +++ b/src/content/tutorial/ui-102/1-intro.md @@ -516,4 +516,4 @@ you'll start building the app in earnest. [Flutter CLI tool]: /reference/flutter-cli [complete the previous tutorial]: /tutorial/set-up-state-app -[`cupertino_icons` package]: https://pub.dev/packages/cupertino_icons \ No newline at end of file +[`cupertino_icons` package]: https://pub.dev/packages/cupertino_icons diff --git a/src/content/tutorial/ui-102/2-adaptive-layout.md b/src/content/tutorial/ui-102/2-adaptive-layout.md index 4fea309817c..5388a0435d5 100644 --- a/src/content/tutorial/ui-102/2-adaptive-layout.md +++ b/src/content/tutorial/ui-102/2-adaptive-layout.md @@ -180,7 +180,7 @@ class RolodexApp extends StatelessWidget { ``` If you're running in Chrome, you can resize the browser window to see -layout changes. +layout changes. ## Add list selection functionality @@ -203,7 +203,7 @@ class AdaptiveLayout extends StatefulWidget { class _AdaptiveLayoutState extends State { - // New + // New int selectedListId = 0; // New @@ -270,7 +270,7 @@ class _AdaptiveLayoutState extends State { final isLargeScreen = constraints.maxWidth > largeScreenMinWidth; if (isLargeScreen) { - return _buildLargeScreenLayout(); // New + return _buildLargeScreenLayout(); // New } else { // For small screens, use the original, navigation-style approach return const ContactGroupsPage(); @@ -279,7 +279,7 @@ class _AdaptiveLayoutState extends State { ); } - // New + // New Widget _buildLargeScreenLayout() { return const CupertinoPageScaffold( backgroundColor: CupertinoColors.extraLightBackgroundGray, diff --git a/src/content/tutorial/ui-102/3-slivers.md b/src/content/tutorial/ui-102/3-slivers.md index ee7e27397e8..f0021fd90a5 100644 --- a/src/content/tutorial/ui-102/3-slivers.md +++ b/src/content/tutorial/ui-102/3-slivers.md @@ -13,7 +13,7 @@ behaviors. By the end of this section, you'll understand how to use `CustomScrollView`, create navigation bars that collapse, and organize content in scrollable sections. -## Slivers and widgets +## Slivers and widgets Slivers are scrollable areas that can be composed together in a `CustomScrollView` or other scroll views. Think of slivers as @@ -254,7 +254,7 @@ class _ContactGroupsView extends StatelessWidget { Widget _buildTrailing(List contacts, BuildContext context) { //... } - + } ``` @@ -265,7 +265,7 @@ and navigation indicators. ## Create advanced scrolling for contacts Now, work on the contacts page. Just like before, you'll create a -private, reusable view to avoid code duplication. +private, reusable view to avoid code duplication. In the next lesson, you'll implement navigation for small screens. To see your progress on the contacts list page in the meantime, update @@ -434,7 +434,7 @@ smoothly transitions into the collapsed navigation bar. Real-world contact apps organize contacts alphabetically. To do this, create sections for each letter. Add the following widget to the bottom of your `contacts.dart` file. This widget doesn't contain any -slivers. +slivers. ```dart // lib/screens/contacts.dart diff --git a/src/content/tutorial/ui-102/4-navigation.md b/src/content/tutorial/ui-102/4-navigation.md index 6ff5d12f0ec..767ac97196d 100644 --- a/src/content/tutorial/ui-102/4-navigation.md +++ b/src/content/tutorial/ui-102/4-navigation.md @@ -76,11 +76,11 @@ This small code block contains the most important new information on this page. `Navigator.of(context)` retrieves the nearest `Navigator` widget from the widget tree. The `push` method adds a new route to the -navigator's stack, and displays the widget returned from the `builder` property. +navigator's stack, and displays the widget returned from the `builder` property. This is the most basic implementation of using stack-based navigation, where new screens are pushed on top of the current screen. To navigate -back to the previous screen, you'd use the `Navigator.pop` method. +back to the previous screen, you'd use the `Navigator.pop` method. `CupertinoPageRoute` creates iOS-style page transitions with the following features: - A slide-in animation from the right. @@ -222,4 +222,4 @@ Hot reload your app and test the navigation: The app automatically chooses the appropriate navigation pattern based on screen size. This provides an optimal experience on both phones and -tablets. \ No newline at end of file +tablets. diff --git a/src/content/tutorial/ui/1-create-an-app.md b/src/content/tutorial/ui/1-create-an-app.md index f2fffd8c77d..911bcec610b 100644 --- a/src/content/tutorial/ui/1-create-an-app.md +++ b/src/content/tutorial/ui/1-create-an-app.md @@ -22,7 +22,7 @@ even mostly work 😀). ## Create a new Flutter project The first step to building Flutter apps is to create a new project. You create -new apps with the [Flutter CLI tool][], installed as part of the Flutter SDK. +new apps with the [Flutter CLI tool][], installed as part of the Flutter SDK. Open your terminal or command prompt and run the following command to create a new Flutter project: @@ -51,7 +51,7 @@ The `main` function is the entry point to any Dart program, and a Flutter app is just a **Dart** program. The `runApp` method is part of the Flutter SDK, and it takes a **widget** as an argument. (Most of this tutorial is about widgets, but in the simplest terms a widget is a Dart object that describes a piece of UI.) -In this case, an instance of the `MainApp` widget is being passed in. +In this case, an instance of the `MainApp` widget is being passed in. Just below the `main` function, you’ll find the `MainApp` class declaration. @@ -93,7 +93,7 @@ $ cd birdle $ flutter run -d chrome ``` -The app will build and launch in a new instance of Chrome. +The app will build and launch in a new instance of Chrome. A screenshot that resembles the popular game Wordle. @@ -112,10 +112,10 @@ child: Text('Hello World!'), Change the text inside the string to anything you want. Then, hot-reload your app by pressing `r` in your terminal where the app is running. The running app -should instantly show your updated text. +should instantly show your updated text. [Flutter CLI tool]: /reference/flutter-cli -[Wordle, the popular New York Times game]: https://www.nytimes.com/games/wordle/index.html +[Wordle, the popular New York Times game]: https://www.nytimes.com/games/wordle/index.html [read more about using pub packages]: {{site.dart-site}}/tools/pub/packages [`flutter_gse`]: {{site.pub}}/packages/flutter_gse diff --git a/src/content/tutorial/ui/2-widget-fundamentals.md b/src/content/tutorial/ui/2-widget-fundamentals.md index f6322f98a88..105dc52b97d 100644 --- a/src/content/tutorial/ui/2-widget-fundamentals.md +++ b/src/content/tutorial/ui/2-widget-fundamentals.md @@ -20,7 +20,7 @@ widget. ## Before you start -This app relies on a bit of game logic that isn't UI-related, and thus is outside the scope of this tutorial. Before you move on, you need to add this logic to your app. +This app relies on a bit of game logic that isn't UI-related, and thus is outside the scope of this tutorial. Before you move on, you need to add this logic to your app. 1. Create a new file in the `lib` directory called `game.dart`. 2. Copy the following code into it and import that code into your `main.dart` file. @@ -47,7 +47,7 @@ class Tile extends StatelessWidget { final String letter; final HitType hitType; - // ... + // ... } ``` @@ -64,7 +64,7 @@ tile). Passing data into the widget is at the core of making widgets reusable. ### `Build` method Finally, there’s the all important `build` method, which must be defined on -every widget, and will always return another widget. +every widget, and will always return another widget. ```dart class Tile extends StatelessWidget { @@ -105,7 +105,7 @@ class MainApp extends StatelessWidget { ``` At the moment, your app will be blank, because the `Tile` widget returns an -empty `Container`, which doesn’t display anything by default. +empty `Container`, which doesn’t display anything by default. ## The `Container` widget @@ -113,12 +113,12 @@ The `Tile` widget consists of three of the most common basic widgets: `Container`, `Center`, and `Text`. [`Container`][] is a convenience widget that wraps several basic styling widgets, like `Padding`, -[`ColoredBox`][], [`SizedBox`][], [`DecoratedBox`][], and many more. +[`ColoredBox`][], [`SizedBox`][], [`DecoratedBox`][], and many more. Because the finished UI contains 25 `Tile` widgets in neat columns and rows, it should have an explicit size. Set the width and height properties on the `Container`. (You could also do this with a `SizedBox` widget, but you’ll use -more properties of the `Container` next.) +more properties of the `Container` next.) ```dart class Tile extends StatelessWidget { @@ -173,7 +173,7 @@ colored border around the white square. When this game is complete, the color of the tile will depend on the user’s guess. The tile will be green when the user has guessed correctly, yellow when the letter is correct but the position is incorrect, and gray if the guess is -wrong on both axes. +wrong on both axes. The following figure shows all three possibilities. @@ -202,7 +202,7 @@ class Tile extends StatelessWidget { HitType.partial => Colors.yellow, HitType.miss => Colors.grey, _ => Colors.white, - }, + }, // TODO: add children ), ); @@ -212,7 +212,7 @@ class Tile extends StatelessWidget { ## Child widgets -Finally, add the `Center` and `Text` widgets to the `Container.child` property. +Finally, add the `Center` and `Text` widgets to the `Container.child` property. Most widgets in the Flutter SDK have a `child` or `children` property that’s meant to be passed a widget or a list of widgets, respectively. It's best @@ -264,7 +264,7 @@ child: Tile('A', HitType.partial) ``` Soon, this small box will be one of many widgets on the screen. In the next -lesson, you’ll start building the game grid itself. +lesson, you’ll start building the game grid itself. diff --git a/src/content/tutorial/ui/3-layout.md b/src/content/tutorial/ui/3-layout.md index 7c51bfd4119..3cd243418de 100644 --- a/src/content/tutorial/ui/3-layout.md +++ b/src/content/tutorial/ui/3-layout.md @@ -12,13 +12,13 @@ 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, including high-level widgets like [`Scaffold`][] and [`AppBar`][], which lay out the structure of a screen, to -lower-level widgets like [`Column`][] or [`Row`][] +lower-level widgets like [`Column`][] or [`Row`][] that lay out widgets vertically or horizontally. ## `Scaffold` and `AppBar` Mobile applications often have a bar at the top called an “app bar” that can -display a title, navigation controls, and/or actions. +display a title, navigation controls, and/or actions. A screenshot of a simple application with a bar across the top that has a title and settings button. @@ -100,7 +100,7 @@ class MainApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( home: Scaffold( - // Changed from `Tile` + // Changed from `Tile` body: Center(child: GamePage()), ), ); @@ -146,12 +146,12 @@ class GamePage extends StatelessWidget { The `spacing` property puts five pixels between each element on the main axis. Within `Column.children`, add one row *for each* element in the `_game.guesses` -list. +list. -:::note +:::note This `guesses` list is a **fixed-size** list, starting with five elements, one for each *potential* guess. The list will always contain exactly five -elements, and therefore will always render five rows. +elements, and therefore will always render five rows. ::: ```dart @@ -209,8 +209,8 @@ time, and it now looks like the following figure. :::note Challenge -Add a `Tile` to each row for each letter allowed in the guess. -The `guess` variable in the loop is a [record][] with the type +Add a `Tile` to each row for each letter allowed in the guess. +The `guess` variable in the loop is a [record][] with the type `({String char, HitType type})`. **Solution:** @@ -220,7 +220,7 @@ class GamePage extends StatelessWidget { const GamePage({super.key}); // This manages game logic, and is out of scope for this lesson final Game _game = Game(); - + Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(8.0), @@ -231,7 +231,7 @@ class GamePage extends StatelessWidget { Row( spacing: 5.0, children: [ - for (var letter in guess) + for (var letter in guess) Tile(letter.char, letter.type), ] ), @@ -254,4 +254,4 @@ When you reload your app, you should see a 5x5 grid of white squares. [`Row`]: {{site.api}}/flutter/widgets/Row-class.html [`Align`]: {{site.api}}/flutter/widgets/Align-class.html [collection-for]: {{site.dart-site}}/language/collections#for-element -[record]: {{site.dart-site}}/language/records \ No newline at end of file +[record]: {{site.dart-site}}/language/records diff --git a/src/content/tutorial/ui/4-devtools.md b/src/content/tutorial/ui/4-devtools.md index 0410b4ba758..58b4f7c6ea6 100644 --- a/src/content/tutorial/ui/4-devtools.md +++ b/src/content/tutorial/ui/4-devtools.md @@ -19,10 +19,10 @@ $ flutter pub global activate devtools # You only need to run this once $ devtools ``` -:::note Run in your IDE +:::note Run in your IDE You can also run DevTools directly inside [VS Code][] and [IntelliJ][], -provided you have the Flutter plugin installed. The screenshots in this lesson +provided you have the Flutter plugin installed. The screenshots in this lesson are from VS Code. ::: @@ -41,10 +41,10 @@ Consider the `GamePage` widget you created in this section: ```dart class GamePage extends StatelessWidget { const GamePage({super.key}); - + final Game _game = Game(); - @override + @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(8.0), diff --git a/src/content/tutorial/ui/5-user-input.md b/src/content/tutorial/ui/5-user-input.md index e0ec555e486..db31be96078 100644 --- a/src/content/tutorial/ui/5-user-input.md +++ b/src/content/tutorial/ui/5-user-input.md @@ -36,17 +36,17 @@ class GuessInput extends StatelessWidget { } ``` -The line `final void Function(String) onSubmitGuess;` -declares a `final` member of the class called `onSubmitGuess` +The line `final void Function(String) onSubmitGuess;` +declares a `final` member of the class called `onSubmitGuess` that has the type `void Function(String)`. This function takes a single `String` argument (the user's guess) and doesn't return any -value (denoted by `void`). +value (denoted by `void`). This callback tells us that the logic that actually handles the user's guess will be written elsewhere. It's good practice for interactive widgets to use callback functions to keep the widget that handles interactions reusable and decoupled from any -specific functionality. +specific functionality. By the end of this lesson, the passed-in `onGuessSubmitted` function is called when a user enters a guess. First, you'll need to build @@ -95,26 +95,26 @@ a `Row` (or `Column`) is wrapped in `Expanded`, it tells that child to fill all the available space along the main axis (horizontal for `Row`, vertical for `Column`) that hasn't been taken by other children. This makes the `TextField` stretch to take up all the space -*except* what’s taken by other widgets in the row. +*except* what’s taken by other widgets in the row. :::tip Tip -`Expanded` is often the solution to "[unbounded width/height][]" exceptions. +`Expanded` is often the solution to "[unbounded width/height][]" exceptions. ::: The `TextField` widget is also new in this lesson and is the star of the show. -This is the basic Flutter widget for text input. +This is the basic Flutter widget for text input. Thus far, `TextField` has the following configuration. * It’s decorated with a rounded border. Notice that the decoration - configuration is very similar to how a `Container` and boxes are decorated. + configuration is very similar to how a `Container` and boxes are decorated. * Its `maxLength` property is set to 5 because the game only allows guesses of 5-letter words. ## Handle text with `TextEditingController` Next, you need a way to manage the text that the user types into the -input field. For this, use a [`TextEditingController`][]. +input field. For this, use a [`TextEditingController`][]. ```dart class GuessInput extends StatelessWidget { @@ -124,7 +124,7 @@ class GuessInput extends StatelessWidget { // NEW final TextEditingController _textEditingController = TextEditingController(); - + @override Widget build(BuildContext context) { return Row( @@ -142,7 +142,7 @@ class GuessInput extends StatelessWidget { ), ), ), - // + // ], ); } @@ -190,7 +190,7 @@ it. The simplest way to react to input is by using the `TextField.onSubmitted` argument. This argument accepts a callback, and the callback is triggered whenever the user presses the "Enter" key on the keyboard while the text field has focus. - + For now, ensure that this works by adding the following callback to `TextField.onSubmitted`. @@ -248,7 +248,7 @@ class GuessInput extends StatelessWidget { final void Function(String) onSubmitGuess; final TextEditingController _textEditingController = TextEditingController(); - + @override Widget build(BuildContext context) { return Row( @@ -267,7 +267,7 @@ class GuessInput extends StatelessWidget { onSubmitted: (_) { // UPDATED print(_textEditingController.text); // Temporary _textEditingController.clear(); // NEW - } + } ), ), ), @@ -284,7 +284,7 @@ without the user taking action. In this app, for example, the only thing a user can do is enter a guess, so the `TextField` should be focused automatically when the app launches. And after the user enters a guess, the focus should stay in the `TextField` so they can -enter their next guess. +enter their next guess. To resolve the first focus issue, set up the `autoFocus` property on the `TextField`. @@ -314,7 +314,7 @@ class GuessInput extends StatelessWidget { onSubmitted: (String input) { print(input); // Temporary _textEditingController.clear(); - } + } ), ), ), @@ -327,7 +327,7 @@ class GuessInput extends StatelessWidget { The second issue requires you to use a [`FocusNode`][] to manage the keyboard focus. You can use `FocusNode` to request that a `TextField` gain focus (making the keyboard appear on mobile), -or to know when a field has focus. +or to know when a field has focus. First, create a `FocusNode` in the `GuessInput` class: @@ -382,7 +382,7 @@ class GuessInput extends StatelessWidget { print(input); // Temporary _textEditingController.clear(); _focusNode.requestFocus(); // NEW - } + } ), ), ), @@ -432,7 +432,7 @@ class GuessInput extends StatelessWidget { onSubmitGuess(_textEditionController.text.trim()) _textEditingController.clear(); _focusNode.requestFocus(); - } + } ), ), ), @@ -442,7 +442,7 @@ class GuessInput extends StatelessWidget { } ``` -:::note +:::note The `trim` function prevents whitespace from being entered; otherwise, the user could enter a four letter word plus a whitespace. ::: @@ -495,9 +495,9 @@ also be a button that can submit the guess. There are many button widgets built into Flutter, like [`TextButton`][], [`ElevatedButton`][], and the button you’ll use now: [`IconButton`][]. All of these buttons (and many other interaction widgets) require two -arguments (in addition to their optional arguments): +arguments (in addition to their optional arguments): -* A callback function passed to `onPressed`. +* A callback function passed to `onPressed`. * A widget that makes up the content of the button (often `Text` or an `Icon`). Add an icon button to the row widget’s children list in the @@ -523,7 +523,7 @@ class GuessInput extends StatelessWidget { Expanded(...), IconButton( padding: EdgeInsets.zero, - icon: Icon(Icons.arrow_circle_up), + icon: Icon(Icons.arrow_circle_up), ), ], ); @@ -562,7 +562,7 @@ class GuessInput extends StatelessWidget { } ``` -This method does the same as the `onSubmitted` callback on the `TextField`. +This method does the same as the `onSubmitted` callback on the `TextField`. :::note Challenge - Share "on submitted" logic. @@ -626,7 +626,7 @@ class GuessInput extends StatelessWidget { } ``` -::: +::: [`TextField`]: {{site.api}}/flutter/material/TextField-class.html diff --git a/src/content/tutorial/ui/6-stateful-widget.md b/src/content/tutorial/ui/6-stateful-widget.md index 3d37653d55a..4914e40399d 100644 --- a/src/content/tutorial/ui/6-stateful-widget.md +++ b/src/content/tutorial/ui/6-stateful-widget.md @@ -41,7 +41,7 @@ Here is the basic `StatefulWidget` structure (don't do anything yet): ```dart class ExampleWidget extends StatefulWidget { ExampleWidget({super.key}); - + @override State createState() => _ExampleWidgetState(); } @@ -60,13 +60,13 @@ To convert the `GamePage` widget (or any other) from a stateless widget to a stateful widget, do the following steps: 1. Change `GamePage` to extend `StatefulWidget` instead of - `StatelessWidget`. + `StatelessWidget`. 2. Create a new class named `_GamePageState`, that extends `State`. This new class will hold the mutable state and the `build` method. Move the `build` method and all properties - *instantiated on the widget* from `GamePage` to the state object. + *instantiated on the widget* from `GamePage` to the state object. 3. Implement the `createState()` method in `GamePage`, which returns - an instance of `_GamePageState`. + an instance of `_GamePageState`. :::tip Quick assists @@ -123,7 +123,7 @@ class _GamePageState extends State { Whenever you mutate a `State` object, you must call [`setState`][] to signal the framework to update the user interface and call the -`State`'s `build` method again. +`State`'s `build` method again. In this app, when a user makes a guess, the word they guessed is saved on the `Game` object, which is a property on the `GamePage` class, and @@ -134,7 +134,7 @@ user’s guess. To implement this, update the callback function passed to `GuessInput`. The function needs to call `setState` and, within `setState`, it needs to execute the logic to determine whether the users -guess was correct. +guess was correct. :::note @@ -176,7 +176,7 @@ class _GamePageState extends State { GuessInput( onSubmitGuess: (String guess) { setState(() { // NEW - _game.guess(guess); + _game.guess(guess); }); }, ), diff --git a/src/content/tutorial/ui/7-implicit-animations.md b/src/content/tutorial/ui/7-implicit-animations.md index c76865de4c3..79754124d3a 100644 --- a/src/content/tutorial/ui/7-implicit-animations.md +++ b/src/content/tutorial/ui/7-implicit-animations.md @@ -115,7 +115,7 @@ specified duration. You can add a bit of customization to an implicit animation by passing it a [`Curve`][]. Different curves will change the speed of the animation -at different points throughout the animation. +at different points throughout the animation. {%- comment %} TODO(ewindmill) diagram {%- endcomment %} @@ -163,7 +163,7 @@ free to try them out by passing different types to the Implicit animations like `AnimatedContainer` are powerful because you just tell the widget what the new state should be, and it handles the "how" of the animation. For complex, custom animations, you can write -your own animated widgets. If you’re curious, read the +your own animated widgets. If you’re curious, read the [animations tutorial](https://docs.flutter.dev/ui/animations/tutorial). [`AnimatedContainer`]: {{site.api}}/flutter/widgets/AnimatedContainer-class.html diff --git a/src/content/ui/accessibility/index.md b/src/content/ui/accessibility/index.md index 825ba7324a4..05e020d7bee 100644 --- a/src/content/ui/accessibility/index.md +++ b/src/content/ui/accessibility/index.md @@ -50,7 +50,7 @@ to people with disabilities. In the U.S., the [Americans with Disabilities Act (ADA)][] prohibits discrimination in public accommodations. [Section 508 of the Rehabilitation Act ][] requires federal agencies and their -contractors to meet WCAG standards for all ICT. +contractors to meet WCAG standards for all ICT. In the EU, the [European Accessibility Act (EAA)][] requires a wide range of public and private sector services to be accessible, primarily using @@ -64,7 +64,7 @@ the [EN 301 549][] as its technical basis. [Americans with Disabilities Act (ADA)]: https://www.ada.gov/ [Section 508 of the Rehabilitation Act]: https://www.section508.gov/ -[European Accessibility Act (EAA)]: https://commission.europa.eu/strategy-and-policy/policies/justice-and-fundamental-rights/disability/union-equality-strategy-rights-persons-disabilities-2021-2030/european-accessibility-act_en +[European Accessibility Act (EAA)]: https://commission.europa.eu/strategy-and-policy/policies/justice-and-fundamental-rights/disability/union-equality-strategy-rights-persons-disabilities-2021-2030/european-accessibility-act_en ## Building with accessibility in mind diff --git a/src/content/ui/accessibility/ui-design-and-styling.md b/src/content/ui/accessibility/ui-design-and-styling.md index b158fb1fbbe..7092119751a 100644 --- a/src/content/ui/accessibility/ui-design-and-styling.md +++ b/src/content/ui/accessibility/ui-design-and-styling.md @@ -58,7 +58,7 @@ For more details on testing, check out the [accessibility testing page](/ui/acce Controls that are too small are hard for many people to interact with and select. Ensure that interactive elements have a large enough tap target to be easily -pressed by users. +pressed by users. Both [Android][] and [iOS][] recommend a minimum tap target size of 48x48 dp and 44x44 pts respectively. diff --git a/src/content/ui/accessibility/web-accessibility.md b/src/content/ui/accessibility/web-accessibility.md index febaf344fa4..585ca1a694c 100644 --- a/src/content/ui/accessibility/web-accessibility.md +++ b/src/content/ui/accessibility/web-accessibility.md @@ -48,7 +48,7 @@ Semantic roles define the purpose of a UI element, helping screen readers and other assistive tools interpret and present your application effectively to users. For example, a role can indicate if a widget is a button, a link, to users. For example, a role can indicate whether a widget is a button, a link, -a heading, a slider, or part of a table. +a heading, a slider, or part of a table. While Flutter's standard widgets often provide these semantics automatically, a custom component without a clearly defined role can be incomprehensible @@ -96,21 +96,21 @@ class MyCustomListWidget extends StatelessWidget { @override Widget build(BuildContext context) { // This example shows how to explicitly assign list and listitem roles - // when building a custom list structure. + // when building a custom list structure. return Semantics( role: SemanticsRole.list, explicitChildNodes: true, - child: Column( + child: Column( children: [ Semantics( - role: SemanticsRole.listItem, + role: SemanticsRole.listItem, child: const Padding( padding: EdgeInsets.all(8.0), child: Text('Content of the first custom list item.'), ), ), Semantics( - role: SemanticsRole.listItem, + role: SemanticsRole.listItem, child: const Padding( padding: EdgeInsets.all(8.0), child: Text('Content of the second custom list item.'), diff --git a/src/content/ui/adaptive-responsive/best-practices.md b/src/content/ui/adaptive-responsive/best-practices.md index 4a5b5d55c8d..6aea1b50154 100644 --- a/src/content/ui/adaptive-responsive/best-practices.md +++ b/src/content/ui/adaptive-responsive/best-practices.md @@ -109,7 +109,7 @@ To summarize: * Android large format tiers require portrait and landscape support at the [lowest level][]. * Android devices can [override a locked screen][] - * Apple guidelines say [aim to support both orientations][] + * Apple guidelines say [aim to support both orientations][] [an accessibility issue]: https://www.w3.org/WAI/WCAG21/Understanding/orientation.html [aim to support both orientations]: https://www.w3.org/WAI/WCAG21/Understanding/orientation.html @@ -168,7 +168,7 @@ To learn how abstracting out device capabilities can help your business logic code, check out the 2022 Google I/O talk, [Flutter lessons for federated plugin development][]. - + [Flutter lessons for federated plugin development]: {{site.youtube-site}}/watch?v=GAnSNplNpCA ### Support a variety of input devices @@ -216,7 +216,7 @@ to change the scroll position on screen rotation. Apps should retain or restore [app state][] as the device rotates, changes window size, -or folds and unfolds. +or folds and unfolds. By default, an app should maintain state. If your app loses state during device configuration, @@ -227,7 +227,7 @@ Some native extensions might lose state when the device changes position. For more information on a real-world case -where this occurred, check out +where this occurred, check out [Problem: Folding/unfolding causes state loss][state-loss] in [Developing Flutter apps for Large screens][article], a free article on Medium. diff --git a/src/content/ui/adaptive-responsive/capabilities.md b/src/content/ui/adaptive-responsive/capabilities.md index 76b75de478f..ad4c098e81c 100644 --- a/src/content/ui/adaptive-responsive/capabilities.md +++ b/src/content/ui/adaptive-responsive/capabilities.md @@ -25,7 +25,7 @@ see if there are unique capabilities to leverage. For example: Apple's App Store and Google's Play Store have different rules that apps need to abide by. Different host operating systems have differing -capabilities across time as well as each other. +capabilities across time as well as each other. Another example is leveraging the web's extremely low barrier for sharing. If you're deploying a web app, @@ -43,7 +43,7 @@ Examples of capabilities include: * The existence of an API * OS-enforced restrictions -* Physical hardware requirements (like a camera) +* Physical hardware requirements (like a camera) ### Policies @@ -54,15 +54,15 @@ Examples of policies include: * App store guidelines * Design preferences * Assets or copy that refers to the host device -* Features enabled on the server side +* Features enabled on the server side -### How to structure policy code +### How to structure policy code The simplest mechanical way is `Platform.isAndroid`, `Platform.isIOS`, and `kIsWeb`. These APIs mechanically let you know where the code is running but have some problems as the app expands where it can run, and -as host platforms add functionality. +as host platforms add functionality. The following guidelines explain best practices when developing the capabilities and policies for your app: @@ -70,15 +70,15 @@ when developing the capabilities and policies for your app: **Avoid using `Platform.isAndroid` and similar functions to make layout decisions or assumptions about what a device can do.** -Instead, describe what you want to branch on in a method. +Instead, describe what you want to branch on in a method. Example: Your app has a link to buy something in a website, but you don't want to show that link on iOS -devices for policy reasons. +devices for policy reasons. ```dart bool shouldAllowPurchaseClick() { - // Banned by Apple App Store guidelines. + // Banned by Apple App Store guidelines. return !Platform.isIOS; } @@ -95,14 +95,14 @@ What did you get by adding an additional layer of indirection? The code makes it more clear why the branched path exists. This method can exist directly in the class but it's likely that other parts of the code might need this same check. -If so, put the code in a class. +If so, put the code in a class. ```dart title="policy.dart" class Policy { bool shouldAllowPurchaseClick() { - // Banned by Apple App Store guidelines. + // Banned by Apple App Store guidelines. return !Platform.isIOS; } } @@ -110,18 +110,18 @@ class Policy { With this code in a class, any widget test can mock `Policy().shouldAllowPurchaseClick` and verify the behavior -independently of where the device runs. +independently of where the device runs. It also means that later, if you decide that buying on the web isn't the right flow for Android users, you can change the implementation -and the tests for clickable text won't need to change. +and the tests for clickable text won't need to change. -## Capabilities +## Capabilities Sometimes you want your code to do something but the API doesn't exist, or maybe you depend on a plugin feature that isn't yet implemented on all of the platforms you support. -This is a limitation of what the device _can_ do. +This is a limitation of what the device _can_ do. Those situations are similar to the policy decisions described above, but these are referred to as _capabilities_. @@ -132,7 +132,7 @@ a logical distinction between what apps _can_ do and what they _should_ do helps larger products respond to changes in what platforms can do or require in addition to your own preferences after -the initial code is written. +the initial code is written. For example, consider the case where one platform adds a new permission that requires users to interact with @@ -145,13 +145,13 @@ then the implementation of `requirePermissionDialogFlow` can now check the API level and return true for platform 2. You've leveraged the work you already did. -## Policies +## Policies We encourage starting with a `Policy` class initially even if it seems like you won't make many policy based decisions. As the complexity of the class grows or the number of inputs expands, you might decide to break up the policy class by feature -or some other criteria. +or some other criteria. For policy implementation, you can use compile time, run time, or Remote Procedure Call (RPC) backed implementations. @@ -166,12 +166,12 @@ a specific payment provider given the content of your app. Runtime checks can be good for determining if there is a touch screen the user can use. Android has a feature you can check and your web implementation could -check for max touch points. +check for max touch points. RPC-backed policy changes are good for incremental -feature rollout or for decisions that might change later. +feature rollout or for decisions that might change later. -## Summary +## Summary Use a `Capability` class to define what the code *can* do. You might check against the existence of an API, @@ -183,7 +183,7 @@ Use a `Policy` class (or classes depending on complexity) to define what the code _should_ do to comply with App store guidelines, design preferences, and assets or copy that need to refer to the host device. -Policies can be a mix of compile, runtime, or RPC checks. +Policies can be a mix of compile, runtime, or RPC checks. Test the branching code by mocking capabilities and policies so the widget tests don't need to change @@ -191,4 +191,3 @@ when capabilities or policies change. Name the methods in your capabilities and policies classes based on what they are trying to branch, rather than on device type. - diff --git a/src/content/ui/adaptive-responsive/general.md b/src/content/ui/adaptive-responsive/general.md index 3b51d54666d..fbd9f09a190 100644 --- a/src/content/ui/adaptive-responsive/general.md +++ b/src/content/ui/adaptive-responsive/general.md @@ -98,7 +98,7 @@ so you would use `MediaQuery.sizeOf`. Previous advice recommended that you use the `of` method of `MediaQuery` to obtain the app window's dimensions. Why has this advice changed? -The short answer is **for performance reasons.** +The short answer is **for performance reasons.** `MediaQuery` contains a lot of data, but if you're only interested in the size property, it's more @@ -160,4 +160,3 @@ the [Building an animated responsive app layout with Material 3][codelab]. The next page discusses how to ensure that your app looks best on large screens and foldables. - diff --git a/src/content/ui/adaptive-responsive/idioms.md b/src/content/ui/adaptive-responsive/idioms.md index 9a25357cf6b..f1542c398b9 100644 --- a/src/content/ui/adaptive-responsive/idioms.md +++ b/src/content/ui/adaptive-responsive/idioms.md @@ -2,7 +2,7 @@ title: Platform idioms description: >- Learn how to create a responsive app - that responds to changes in the screen size. + that responds to changes in the screen size. shortTitle: Idioms --- @@ -393,7 +393,7 @@ include the following: * Hook into `onPan` gesture events, and move an object yourself within a parent `Stack`. -* Use one of the [pre-made list packages][] on pub.dev. +* Use one of the [pre-made list packages][] on pub.dev. [`Draggable`]: {{site.api}}/flutter/widgets/Draggable-class.html [`DragTarget`]: {{site.api}}/flutter/widgets/DragTarget-class.html diff --git a/src/content/ui/adaptive-responsive/input.md b/src/content/ui/adaptive-responsive/input.md index 7a723d13a73..92a9b181db2 100644 --- a/src/content/ui/adaptive-responsive/input.md +++ b/src/content/ui/adaptive-responsive/input.md @@ -300,9 +300,9 @@ for standard button and text cursors. of the Material buttons to set the `overlayColor` to transparent.) Implement a focus state for any custom buttons or -gesture detectors in your app. +gesture detectors in your app. If you change the default Material button styles, -test for keyboard focus states and +test for keyboard focus states and implement your own, if needed. To change the cursor from within your custom widgets, @@ -420,4 +420,3 @@ see the [Material Design guide][]. [Material Design guide]: {{site.material2}}/design/layout/applying-density.html#usage [`VisualDensity`]: {{site.api}}/flutter/material/VisualDensity-class.html - diff --git a/src/content/ui/adaptive-responsive/large-screens.md b/src/content/ui/adaptive-responsive/large-screens.md index 526e2912e41..0a82452e00f 100644 --- a/src/content/ui/adaptive-responsive/large-screens.md +++ b/src/content/ui/adaptive-responsive/large-screens.md @@ -32,7 +32,7 @@ For example, it: * Increases your app's visibility in the Play Store. Recent [Play Store updates][] show ratings by device type and indicates when an app lacks - large screen support. + large screen support. * Ensures that your app meets iPadOS submission guidelines and is [accepted in the App Store][]. ::: @@ -100,17 +100,17 @@ that you can use, namely: [`SliverGridDelegateWithFixedCrossAxisCount`][] : Lets you assign a specific number of columns to your grid. -[`SliverGridDelegateWithMaxCrossAxisExtent`][] +[`SliverGridDelegateWithMaxCrossAxisExtent`][] : Lets you define a max item width. -[`SliverGridDelegateWithFixedCrossAxisCount`]: {{site.api}}/flutter/rendering/SliverGridDelegateWithFixedCrossAxisCount-class.html +[`SliverGridDelegateWithFixedCrossAxisCount`]: {{site.api}}/flutter/rendering/SliverGridDelegateWithFixedCrossAxisCount-class.html [`SliverGridDelegateWithMaxCrossAxisExtent`]: {{site.api}}/flutter/rendering/SliverGridDelegateWithMaxCrossAxisExtent-class.html :::secondary Don't use the grid delegate for these classes that lets you set the column count directly and then hardcode the number of columns based on whether the device -is a tablet, or whatever. +is a tablet, or whatever. The number of columns should be based on the size of the window and not the size of the physical device. @@ -184,7 +184,7 @@ How to obtain the physical screen dimensions? You can use the [`Display`][] API, introduced in Flutter 3.13, which contains the size, -pixel ratio, and the refresh rate of the physical device. +pixel ratio, and the refresh rate of the physical device. [`Display`]: {{site.api}}/flutter/dart-ui/Display-class.html @@ -252,4 +252,3 @@ check out [Problem: Navigation rail][], a section in the [`BottomNavigationBar`]: {{site.api}}/flutter/material/BottomNavigationBar-class.html [`NavigationRail`]: {{site.api}}/flutter/material/NavigationRail-class.html [Problem: Navigation rail]: {{site.flutter-medium}}/developing-flutter-apps-for-large-screens-53b7b0e17f10#:~:text=Problem%3A%20Navigation%20rail1 - diff --git a/src/content/ui/adaptive-responsive/safearea-mediaquery.md b/src/content/ui/adaptive-responsive/safearea-mediaquery.md index 55c4f4984d1..e3c18bea865 100644 --- a/src/content/ui/adaptive-responsive/safearea-mediaquery.md +++ b/src/content/ui/adaptive-responsive/safearea-mediaquery.md @@ -65,7 +65,7 @@ of your device's display, such as having a hinge or a fold. how much to inset its child `Widget`. Specifically, it uses the `MediaQuery` padding property, which is basically the amount of the display that's -partially obscured by system UI, display notches, or status bar. +partially obscured by system UI, display notches, or status bar. So, why not use `MediaQuery` directly? @@ -76,7 +76,7 @@ to `SafeArea`'s children to make it appear as if the padding added to `SafeArea` doesn't exist. This means that you can nest `SafeArea`s, and only the topmost one will apply the padding -needed to avoid the notches as system UI. +needed to avoid the notches as system UI. As your app grows and you move widgets around, you don't have to worry about having too much @@ -84,12 +84,12 @@ padding applied if you have multiple `SafeArea`s, whereas you would have issues if using `MediaQueryData.padding` directly. -You _can_ wrap the body of a `Scaffold` widget +You _can_ wrap the body of a `Scaffold` widget with a `SafeArea`, but you don't _have_ to put it this high in the widget tree. The `SafeArea` just needs to wrap the contents that would cause information loss if cut off by the -hardware features mentioned earlier. +hardware features mentioned earlier. For example, if you purposefully want your app to stretch under the cutouts, you can move the `SafeArea` to wrap diff --git a/src/content/ui/animations/index.md b/src/content/ui/animations/index.md index 39c6d4986a0..38d767cac92 100644 --- a/src/content/ui/animations/index.md +++ b/src/content/ui/animations/index.md @@ -315,9 +315,9 @@ colorTween = ColorTween(begin: Colors.transparent, end: Colors.black54); ``` A `Tween` object doesn't store any state. Instead, it provides the -[`evaluate(Animation animation)`][] method that uses the +[`evaluate(Animation animation)`][] method that uses the `transform` function to map the current value of the animation -(between 0.0 and 1.0), to the actual animation value. +(between 0.0 and 1.0), to the actual animation value. The current value of the `Animation` object can be found in the `.value` method. The evaluate function also performs some housekeeping, diff --git a/src/content/ui/animations/overview.md b/src/content/ui/animations/overview.md index 11dcb6ae8c2..27394389338 100644 --- a/src/content/ui/animations/overview.md +++ b/src/content/ui/animations/overview.md @@ -139,7 +139,7 @@ A `Ticker` can be started and stopped. When started, it returns a `Future` that will resolve when it is stopped. Each tick, the `Ticker` provides the callback with the -duration since the first tick after it was started. +duration since the first tick after it was started. Because tickers always give their elapsed time relative to the first tick after they were started; tickers are all synchronised. If you diff --git a/src/content/ui/design/cupertino/index.md b/src/content/ui/design/cupertino/index.md index 111b1f11329..c356b2cc05e 100644 --- a/src/content/ui/design/cupertino/index.md +++ b/src/content/ui/design/cupertino/index.md @@ -69,4 +69,3 @@ flutter run lib/cupertino/switch/cupertino_switch.0.dart [Cupertino library]: {{site.api}}/flutter/cupertino/cupertino-library.html [Cupertino widget catalog]: /ui/widgets/cupertino [Instructions]: {{site.github}}/flutter/flutter/tree/main/examples/api#api-example-code - diff --git a/src/content/ui/design/graphics/index.md b/src/content/ui/design/graphics/index.md index f73c442841c..a5f4ac4743e 100644 --- a/src/content/ui/design/graphics/index.md +++ b/src/content/ui/design/graphics/index.md @@ -3,7 +3,7 @@ layout: toc title: Custom drawing and graphics shortTitle: Drawing & graphics description: > - Content covering how to create custom graphics and + Content covering how to create custom graphics and use your own shaders in Flutter apps. sitemap: false --- diff --git a/src/content/ui/design/text/typography.md b/src/content/ui/design/text/typography.md index 33ab1baead8..7b6d1b883f6 100644 --- a/src/content/ui/design/text/typography.md +++ b/src/content/ui/design/text/typography.md @@ -6,28 +6,28 @@ description: Learn about Flutter's support for typography. [_Typography_][] covers the style and appearance of type or fonts: it specifies how heavy the font is, the slant of the font, the spacing between -the letters, and other visual aspects of the text. +the letters, and other visual aspects of the text. -All fonts are _not_ created the same. +All fonts are _not_ created the same. -A font style is defined by, at minimum, a typeface, representing the set of -common character rules describing fonts in the same type family, such as -**Roboto** or **Noto**, a font weight (for example, Regular, Bold, or a -numeric value), and a style (like Regular, _Italic_, etc). All of these -and additional pre-set attributes come together to make up +A font style is defined by, at minimum, a typeface, representing the set of +common character rules describing fonts in the same type family, such as +**Roboto** or **Noto**, a font weight (for example, Regular, Bold, or a +numeric value), and a style (like Regular, _Italic_, etc). All of these +and additional pre-set attributes come together to make up what we would call a static font. -Variable fonts allow some of these attributes to be modified at runtime and +Variable fonts allow some of these attributes to be modified at runtime and store what would normally be multiple static fonts in a single file. [_Typography_]: https://en.wikipedia.org/wiki/Typography ## Typographic Scale -A typographical scale is a set of related text styles to provide balance, +A typographical scale is a set of related text styles to provide balance, cohesion, and visual variety in your apps. -The common type scale in Flutter, provided by [`TextTheme`][], includes five +The common type scale in Flutter, provided by [`TextTheme`][], includes five categories of text indicating the function: * Display @@ -39,16 +39,16 @@ categories of text indicating the function: There are also three size variations for each: * Small -* Medium +* Medium * Large -Each of these fifteen combinations of a category and text size are represented -by a single [`TextStyle`][]. +Each of these fifteen combinations of a category and text size are represented +by a single [`TextStyle`][]. Listing of typographical scale for Material TextTheme -All the platform specific typographical scales that Flutter exposes are -contained in the [`Typography`][] class. Usually, you will not need to +All the platform specific typographical scales that Flutter exposes are +contained in the [`Typography`][] class. Usually, you will not need to reference this class directly as the `TextTheme` will be localized to your target platform. [`TextTheme`]: https://api.flutter.dev/flutter/material/TextTheme-class.html @@ -68,8 +68,8 @@ when specifying the type. ### Using the Google Fonts type tester -A growing number of fonts on Google Fonts offer some variable font capabilities. -You can see the range of options by using the Type Tester and see how you +A growing number of fonts on Google Fonts offer some variable font capabilities. +You can see the range of options by using the Type Tester and see how you might vary a single font. Demonstration of varying aspects for Noto Sans with Lorem ipsum text @@ -93,7 +93,7 @@ Once again, the Google Fonts site can help. ### Using the Google Fonts package -While you can download fonts from the site and install them manually in your apps, +While you can download fonts from the site and install them manually in your apps, you can elect to use theme directly from the [google_fonts][] package on [pub.dev][]. They can be used as is by referencing simply the font name: @@ -128,7 +128,7 @@ to support the feature): * [`FontFeature`][] to select glyphs * [`FontWeight`][] to modify weight * [`FontStyle`][] to italicize -* [`FontVariation`][] to specify a range of values for a specific property. +* [`FontVariation`][] to specify a range of values for a specific property. A `FontFeature` corresponds to an [OpenType feature tag][] and can be thought of as a boolean flag to enable or disable diff --git a/src/content/ui/internationalization/index.md b/src/content/ui/internationalization/index.md index 2307c7c07c1..b3782fd8b27 100644 --- a/src/content/ui/internationalization/index.md +++ b/src/content/ui/internationalization/index.md @@ -58,7 +58,7 @@ $ flutter create ``` To use `flutter_localizations`, -add the package as a dependency to your `pubspec.yaml` file, +add the package as a dependency to your `pubspec.yaml` file, as well as the `intl` package: ```console @@ -145,7 +145,7 @@ structured, is covered on this page. for the `Localizations` widget that allows for (the typically rare) situation where a section of your application needs to be localized to a different locale than the locale -configured for your device. +configured for your device. To observe this behavior, add a call to `Localizations.override` and a simple `CalendarDatePicker`: @@ -203,7 +203,7 @@ complete the following instructions: $ flutter pub add intl:any ``` -2. Open the `pubspec.yaml` file and enable the `generate` flag. +2. Open the `pubspec.yaml` file and enable the `generate` flag. This flag is found in the `flutter` section in the pubspec file. @@ -225,10 +225,10 @@ complete the following instructions: This file configures the localization tool. In this example, you've done the following: - + * Put the [App Resource Bundle][] (`.arb`) input files in `${FLUTTER_PROJECT}/lib/l10n`. - The `.arb` provide localization resources for your app. + The `.arb` provide localization resources for your app. * Set the English template as `app_en.arb`. * Told Flutter to generate localizations in the `app_localizations.dart` file. @@ -350,7 +350,7 @@ return MaterialApp( :::tip When using VS Code, add the [arb-editor extension][]. -This extension adds syntax highlighting, snippets, +This extension adds syntax highlighting, snippets, diagnostics, and quick fixes to help edit `.arb` template files. ::: @@ -415,9 +415,9 @@ English, for example, pluralizes "person" to "people", but that doesn't go far enough. The `message0` plural might be "no people" or "zero people". The `messageFew` plural might be -"several people", "some people", or "a few people". +"several people", "some people", or "a few people". The `messageMany` plural might -be "most people" or "many people", or "a crowd". +be "most people" or "many people", or "a crowd". Only the more general `messageOther` field is required. The following example shows what options are available: @@ -553,7 +553,7 @@ The resulting string is as follows: ### Messages with numbers and currencies Numbers, including those that represent currency values, -are displayed very differently in different locales. +are displayed very differently in different locales. The localizations generation tool in `flutter_localizations` uses the [`NumberFormat`]({{site.api}}/flutter/intl/NumberFormat-class.html) @@ -609,7 +609,7 @@ make the following changes to the `lib/l10n/app_en.arb` file: ### Messages with dates Dates strings are formatted in many different ways -depending both the locale and the app's needs. +depending both the locale and the app's needs. Placeholder values with type `DateTime` are formatted with [`DateFormat`][] in the `intl` package. @@ -1175,7 +1175,7 @@ with a different i18n framework. Complete source code for the [`minimal`][] app. -In the following example, the `DemoLocalizations` class +In the following example, the `DemoLocalizations` class includes all of its translations directly in per language Maps: @@ -1302,4 +1302,3 @@ check out [Using the Dart intl tools](#dart-tools). [`intl_example`]: {{site.repo.this}}/tree/{{site.branch}}/examples/internationalization/intl_example [`minimal`]: {{site.repo.this}}/tree/{{site.branch}}/examples/internationalization/minimal - diff --git a/src/content/ui/layout/constraints.md b/src/content/ui/layout/constraints.md index d0bddae50ce..c15ac3627a6 100644 --- a/src/content/ui/layout/constraints.md +++ b/src/content/ui/layout/constraints.md @@ -124,7 +124,7 @@ pass their constraint on to their children. Generally, there are three kinds of boxes, in terms of how they handle their constraints: - + * Those that try to be as big as possible. For example, the boxes used by [`Center`][] and [`ListView`][]. @@ -144,7 +144,7 @@ for instance, it tries to honor that and be that particular size. Others, for example [`Row`][] and [`Column`][] (flex boxes) vary based on the constraints they are given, as described in the [Flex](#flex) section. - + [`Center`]: {{site.api}}/flutter/widgets/Center-class.html [`Column`]: {{site.api}}/flutter/widgets/Column-class.html [`Container`]: {{site.api}}/flutter/widgets/Container-class.html diff --git a/src/content/ui/layout/tutorial.md b/src/content/ui/layout/tutorial.md index fde2e8d2030..79638b341c5 100644 --- a/src/content/ui/layout/tutorial.md +++ b/src/content/ui/layout/tutorial.md @@ -130,12 +130,12 @@ In this section, shell out the basic Flutter app code to start your app. ```dart import 'package:flutter/material.dart'; - + void main() => runApp(const MyApp()); - + class MyApp extends StatelessWidget { const MyApp({super.key}); - + @override Widget build(BuildContext context) { const String appTitle = 'Flutter layout demo'; diff --git a/src/content/ui/navigation/index.md b/src/content/ui/navigation/index.md index a7437db68b5..923cd36bfff 100644 --- a/src/content/ui/navigation/index.md +++ b/src/content/ui/navigation/index.md @@ -10,7 +10,7 @@ requirements should also use the [`Router`][] to correctly handle deep links on Android and iOS, and to stay in sync with the address bar when the app is running on the web. -To configure your Android or iOS application to handle deep links, see +To configure your Android or iOS application to handle deep links, see [Deep linking][]. ## Using the Navigator From f02fb1849a9f8998da7efe8bb8f75634e64656fa Mon Sep 17 00:00:00 2001 From: Pierre-Louis Date: Thu, 30 Oct 2025 15:53:45 +0100 Subject: [PATCH 4/5] format .html --- .../docs/get-started/setup-next-steps.html | 13 +++++++++---- src/content/search-all.html | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/_includes/docs/get-started/setup-next-steps.html b/src/_includes/docs/get-started/setup-next-steps.html index 3a05a7accf6..000ace38e37 100644 --- a/src/_includes/docs/get-started/setup-next-steps.html +++ b/src/_includes/docs/get-started/setup-next-steps.html @@ -1,7 +1,8 @@
  • - Discover Flutter widgets + Discover Flutter + widgets
  • Explore samples & tutorials @@ -60,7 +64,8 @@
    - +
    Stay up to date with Flutter diff --git a/src/content/search-all.html b/src/content/search-all.html index 3e8a2bb0ba7..8f7c0056340 100644 --- a/src/content/search-all.html +++ b/src/content/search-all.html @@ -7,11 +7,11 @@ ---

    -Use this search when you want results from multiple Flutter-related sites. + Use this search when you want results from multiple Flutter-related sites.

    -Want only results from docs.flutter.dev? Search docs.flutter.dev. + Want only results from docs.flutter.dev? Search docs.flutter.dev.

    From 304e70492dc0fb605f5f3ffc5e484c3690afa3bf Mon Sep 17 00:00:00 2001 From: Pierre-Louis Date: Thu, 30 Oct 2025 16:53:40 +0100 Subject: [PATCH 5/5] dart run dash_site refresh-excerpts --- src/content/cookbook/design/fonts.md | 10 ++++----- .../cookbook/games/firestore-multiplayer.md | 4 ++-- .../cookbook/plugins/google-mobile-ads.md | 2 +- .../cookbook/testing/widget/orientation.md | 14 ++++++------ .../get-started/flutter-for/android-devs.md | 2 +- .../get-started/flutter-for/swiftui-devs.md | 12 +++++----- .../get-started/flutter-for/uikit-devs.md | 2 +- .../flutter-for/xamarin-forms-devs.md | 2 +- .../packages-and-plugins/using-packages.md | 22 +++++++++---------- .../android/platform-views.md | 4 ++-- .../macos/platform-views.md | 2 +- .../breaking-changes/plugin-api-migration.md | 4 ++-- src/content/testing/code-debugging.md | 2 +- .../testing/integration-tests/index.md | 16 +++++++------- src/content/ui/layout/tutorial.md | 6 ++--- 15 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/content/cookbook/design/fonts.md b/src/content/cookbook/design/fonts.md index 0a150e444b1..69763a7358a 100644 --- a/src/content/cookbook/design/fonts.md +++ b/src/content/cookbook/design/fonts.md @@ -361,12 +361,12 @@ Download the Raleway and RobotoMono font files from [Google Fonts][]. ```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( @@ -377,10 +377,10 @@ Download the Raleway and RobotoMono font files from [Google Fonts][]. ); } } - + class MyHomePage extends StatelessWidget { const MyHomePage({super.key}); - + @override Widget build(BuildContext context) { return Scaffold( diff --git a/src/content/cookbook/games/firestore-multiplayer.md b/src/content/cookbook/games/firestore-multiplayer.md index 2f712225c3c..db511bd9cf0 100644 --- a/src/content/cookbook/games/firestore-multiplayer.md +++ b/src/content/cookbook/games/firestore-multiplayer.md @@ -136,7 +136,7 @@ Dart code in that guide, return to this recipe. ```dart import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_core/firebase_core.dart'; - + import 'firebase_options.dart'; ``` @@ -146,7 +146,7 @@ Dart code in that guide, return to this recipe. ```dart WidgetsFlutterBinding.ensureInitialized(); - + await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); ``` diff --git a/src/content/cookbook/plugins/google-mobile-ads.md b/src/content/cookbook/plugins/google-mobile-ads.md index a2311945a61..fb3b6efabec 100644 --- a/src/content/cookbook/plugins/google-mobile-ads.md +++ b/src/content/cookbook/plugins/google-mobile-ads.md @@ -149,7 +149,7 @@ You need to initialize the Mobile Ads SDK before loading ads. void main() async { WidgetsFlutterBinding.ensureInitialized(); unawaited(MobileAds.instance.initialize()); - + runApp(const MyApp()); } ``` diff --git a/src/content/cookbook/testing/widget/orientation.md b/src/content/cookbook/testing/widget/orientation.md index ecd1ec11a0d..75a7cd9ffa0 100644 --- a/src/content/cookbook/testing/widget/orientation.md +++ b/src/content/cookbook/testing/widget/orientation.md @@ -51,7 +51,7 @@ group your future orientation tests: import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:orientation_tests/main.dart'; - + void main() { group('Orientation', () { // ··· @@ -74,7 +74,7 @@ only `2` columns of data appear in the app: testWidgets('Displays 2 columns in portrait mode', (tester) async { // Build the app. await tester.pumpWidget(const MyApp()); - + // Change to portrait. tester.view.physicalSize = const Size(600, 800); tester.view.devicePixelRatio = 1.0; @@ -82,13 +82,13 @@ only `2` columns of data appear in the app: tester.view.resetPhysicalSize(); }); await tester.pump(); - + // Verify initial orientation is portrait. final orientation = MediaQuery.of( tester.element(find.byType(OrientationList)), ).orientation; expect(orientation, Orientation.portrait); - + // Verify there are only 2 columns in portrait mode. final gridViewFinder = find.byType(GridView); final gridView = tester.widget(gridViewFinder); @@ -113,7 +113,7 @@ only `3` columns of data appear in the app: testWidgets('Displays 3 columns in landscape mode', (tester) async { // Build the app. await tester.pumpWidget(const MyApp()); - + // Change to landscape. tester.view.physicalSize = const Size(800, 600); tester.view.devicePixelRatio = 1.0; @@ -121,13 +121,13 @@ only `3` columns of data appear in the app: tester.view.resetPhysicalSize(); }); await tester.pump(); - + // Verify initial orientation is landscape. final orientation = MediaQuery.of( tester.element(find.byType(OrientationList)), ).orientation; expect(orientation, Orientation.landscape); - + // Verify there are only 3 columns in landscape mode. final gridViewFinder = find.byType(GridView); final gridView = tester.widget(gridViewFinder); diff --git a/src/content/get-started/flutter-for/android-devs.md b/src/content/get-started/flutter-for/android-devs.md index b7a65dcdbff..188867d9b92 100644 --- a/src/content/get-started/flutter-for/android-devs.md +++ b/src/content/get-started/flutter-for/android-devs.md @@ -1582,7 +1582,7 @@ In Flutter there are two ways of adding touch listeners: ```dart class SampleTapApp extends StatelessWidget { const SampleTapApp({super.key}); - + @override Widget build(BuildContext context) { return Scaffold( diff --git a/src/content/get-started/flutter-for/swiftui-devs.md b/src/content/get-started/flutter-for/swiftui-devs.md index 368a4965dc8..fc4092bf72a 100644 --- a/src/content/get-started/flutter-for/swiftui-devs.md +++ b/src/content/get-started/flutter-for/swiftui-devs.md @@ -685,9 +685,9 @@ with two classes that help you draw: ```dart dartpad="978d64ee66d54177fb639f8a9f801039" class SignaturePainter extends CustomPainter { SignaturePainter(this.points); - + final List points; - + @override void paint(Canvas canvas, Size size) { final Paint paint = Paint() @@ -700,7 +700,7 @@ with two classes that help you draw: } } } - + @override bool shouldRepaint(SignaturePainter oldDelegate) => oldDelegate.points != points; @@ -752,10 +752,10 @@ call your navigation routes using their names. // Defines the route name as a constant // so that it's reusable. const detailsPageRouteName = '/details'; - + class App extends StatelessWidget { const App({super.key}); - + @override Widget build(BuildContext context) { return CupertinoApp( @@ -806,7 +806,7 @@ call your navigation routes using their names. ```dart dartpad="d8b22d4dcbefdc8a2e21f1382cf7dc2a" class DetailsPage extends StatelessWidget { const DetailsPage({super.key}); - + @override Widget build(BuildContext context) { // Read the person instance from the arguments. diff --git a/src/content/get-started/flutter-for/uikit-devs.md b/src/content/get-started/flutter-for/uikit-devs.md index 9308cf6e20e..1da231fad6e 100644 --- a/src/content/get-started/flutter-for/uikit-devs.md +++ b/src/content/get-started/flutter-for/uikit-devs.md @@ -1131,7 +1131,7 @@ In Flutter, there are two ways of adding touch listeners: ```dart class SampleTapApp extends StatelessWidget { const SampleTapApp({super.key}); - + @override Widget build(BuildContext context) { return Scaffold( diff --git a/src/content/get-started/flutter-for/xamarin-forms-devs.md b/src/content/get-started/flutter-for/xamarin-forms-devs.md index 223a22db563..f2f3ab6c76c 100644 --- a/src/content/get-started/flutter-for/xamarin-forms-devs.md +++ b/src/content/get-started/flutter-for/xamarin-forms-devs.md @@ -1627,7 +1627,7 @@ In Flutter there are two very similar ways: ```dart class SampleApp extends StatelessWidget { const SampleApp({super.key}); - + @override Widget build(BuildContext context) { return Scaffold( diff --git a/src/content/packages-and-plugins/using-packages.md b/src/content/packages-and-plugins/using-packages.md index 425c4aac4d1..7c39838dc12 100644 --- a/src/content/packages-and-plugins/using-packages.md +++ b/src/content/packages-and-plugins/using-packages.md @@ -392,23 +392,23 @@ To use this package: ```dart import 'package:css_colors/css_colors.dart'; import 'package:flutter/material.dart'; - + void main() { runApp(const MyApp()); } - + class MyApp extends StatelessWidget { const MyApp({super.key}); - + @override Widget build(BuildContext context) { return const MaterialApp(home: DemoPage()); } } - + class DemoPage extends StatelessWidget { const DemoPage({super.key}); - + @override Widget build(BuildContext context) { return Scaffold(body: Container(color: CSSColors.orange)); @@ -453,27 +453,27 @@ To use this plugin: ```dart import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; - + void main() { runApp(const MyApp()); } - + class MyApp extends StatelessWidget { const MyApp({super.key}); - + @override Widget build(BuildContext context) { return const MaterialApp(home: DemoPage()); } } - + class DemoPage extends StatelessWidget { const DemoPage({super.key}); - + void launchURL() { launchUrl(Uri.parse('https://flutter.dev')); } - + @override Widget build(BuildContext context) { return Scaffold( diff --git a/src/content/platform-integration/android/platform-views.md b/src/content/platform-integration/android/platform-views.md index 42698ae7a3c..37b780ea3de 100644 --- a/src/content/platform-integration/android/platform-views.md +++ b/src/content/platform-integration/android/platform-views.md @@ -87,7 +87,7 @@ use the following instructions: const String viewType = ''; // Pass parameters to the platform side. const Map creationParams = {}; - + return PlatformViewLink( viewType: viewType, surfaceFactory: (context, controller) { @@ -148,7 +148,7 @@ use the following instructions: const String viewType = ''; // Pass parameters to the platform side. final Map creationParams = {}; - + return AndroidView( viewType: viewType, layoutDirection: TextDirection.ltr, diff --git a/src/content/platform-integration/macos/platform-views.md b/src/content/platform-integration/macos/platform-views.md index 1e7ffa547fd..a2ddb4fea93 100644 --- a/src/content/platform-integration/macos/platform-views.md +++ b/src/content/platform-integration/macos/platform-views.md @@ -60,7 +60,7 @@ shown in `native_view_example.dart`: const String viewType = ''; // Pass parameters to the platform side. final Map creationParams = {}; - + return AppKitView( viewType: viewType, layoutDirection: TextDirection.ltr, diff --git a/src/content/release/breaking-changes/plugin-api-migration.md b/src/content/release/breaking-changes/plugin-api-migration.md index 11e158d52ed..0756c588c78 100644 --- a/src/content/release/breaking-changes/plugin-api-migration.md +++ b/src/content/release/breaking-changes/plugin-api-migration.md @@ -253,10 +253,10 @@ but aren't required. ```dart import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; - + void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - + testWidgets('Can get battery level', (tester) async { final Battery battery = Battery(); final int batteryLevel = await battery.batteryLevel; diff --git a/src/content/testing/code-debugging.md b/src/content/testing/code-debugging.md index 7ac5f045e71..b83a568161b 100644 --- a/src/content/testing/code-debugging.md +++ b/src/content/testing/code-debugging.md @@ -673,7 +673,7 @@ segments of Dart code, use `dart:developer` [Timeline][] utilities. ```dart import 'dart:developer'; - + void main() { Timeline.startSync('interesting function'); // iWonderHowLongThisTakes(); diff --git a/src/content/testing/integration-tests/index.md b/src/content/testing/integration-tests/index.md index c8b17e61665..ea0aeea5e1e 100644 --- a/src/content/testing/integration-tests/index.md +++ b/src/content/testing/integration-tests/index.md @@ -207,29 +207,29 @@ and your app's Dart file. import 'package:flutter_test/flutter_test.dart'; import 'package:how_to/main.dart'; import 'package:integration_test/integration_test.dart'; - + void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - + group('end-to-end test', () { testWidgets('tap on the floating action button, verify counter', ( tester, ) async { // Load app widget. await tester.pumpWidget(const MyApp()); - + // Verify the counter starts at 0. expect(find.text('0'), findsOneWidget); - + // Finds the floating action button to tap on. final fab = find.byKey(const ValueKey('increment')); - + // Emulate a tap on the floating action button. await tester.tap(fab); - + // Trigger a frame. await tester.pumpAndSettle(); - + // Verify the counter increments by 1. expect(find.text('1'), findsOneWidget); }); @@ -438,7 +438,7 @@ To test in a web browser, perform the following steps. ```dart title="test_driver/integration_test.dart" import 'package:integration_test/integration_test_driver.dart'; - + Future main() => integrationDriver(); ``` diff --git a/src/content/ui/layout/tutorial.md b/src/content/ui/layout/tutorial.md index 79638b341c5..fde2e8d2030 100644 --- a/src/content/ui/layout/tutorial.md +++ b/src/content/ui/layout/tutorial.md @@ -130,12 +130,12 @@ In this section, shell out the basic Flutter app code to start your app. ```dart import 'package:flutter/material.dart'; - + void main() => runApp(const MyApp()); - + class MyApp extends StatelessWidget { const MyApp({super.key}); - + @override Widget build(BuildContext context) { const String appTitle = 'Flutter layout demo';